/*---------------------------------------------------------------------------*
 * Copyright (C) 2012 Ryan M. Murphy <ryan.m.murphy.77@gmail.com>
 *
 * File Name   : usr_bms.c
 *
 * Project     : Watts-rock
 *               <http://code.google.com/p/watts-rock-motherboard/>
 *
 * Description : This file is responsible for performing the operations that
 *               make up the Watts-rock battery management system.
 *
 * Last Update : Oct, 18, 2012
 *---------------------------------------------------------------------------*/

/*---------------------------------------------------------------------------*
 * Include Files
 *---------------------------------------------------------------------------*/
#include <stdio.h>
#include <string.h>
#include <math.h>
#include "arb_error.h"
#include "arb_thread.h"
#include "arb_semaphore.h"
#include "arb_device.h"
#include "arb_sysTimer.h"
#include "arb_mailbox.h"
#include "arb_printf.h"
#include "drv_signal.h"
#include "usr_bms.h"
#include "utl_stateMachine.h"
#include "drv_bms.h"
#include "utl_gpio.h"
#include "utl_eeprom.h"
#include "drv_wifly.h"
#include "usr_hmi.h"
#include "utl_timer.h"
#include "drv_lcd.h"

/*---------------------------------------------------------------------------*
 * Private Defines
 *---------------------------------------------------------------------------*/
#define BMS_SOFTWARE_VERSION_NUMBER       ("april_7_2013_v1")
#define BMS_DEFAULT_BATTERY_SERIAL_NUMBER (0xFFFFFFFF)
#define BMS_DEFAULT_BOARD_SERIAL_NUMBER   (0xFFFFFFFF)

/*---------------------------------------------------------------------------*
 * The thread update rate in system timer ticks
 *---------------------------------------------------------------------------*/
#define BMS_SLEEP_TICKS        (2)
#define BMS_DEEP_DIST_DURATION (50) /*Seconds*/
#define BMS_MAILBOX_SIZE       (36)

/*---------------------------------------------------------------------------*
 * Factory default battery voltage, current, and temperature limits
 *---------------------------------------------------------------------------*/
#define BMS_MIN_HIGH_CURRENT_THR (1.0f)
#define BMS_MAX_HIGH_CURRENT_THR (30.0f)
#define BMS_MIN_LOW_VOLTAGE_THR  (40.0f)
#define BMS_MAX_LOW_VOLTAGE_THR  (45.0f)
#define BMS_MIN_HIGH_VOLTAGE_THR (50.0f)
#define BMS_MAX_HIGH_VOLTAGE_THR (55.0f)
#define BMS_MIN_HIGH_TEMP_THR    (80.0f)
#define BMS_MAX_HIGH_TEMP_THR    (120.0f)
#define BMS_MIN_VOLT_HYST        (3.0f)
#define BMS_MAX_VOLT_HYST        (10.0f)
#define BMS_MIN_TEMP_HYST        (3.0f)
#define BMS_MAX_TEMP_HYST        (50.0f)
#define BMS_MIN_BATT_AMP_HR      (3.0f)
#define BMS_MAX_BATT_AMP_HR      (50.0f)
#define BMS_MAX_CHRG_CURRENT     (6.0f)
#define BMS_MIN_CHRG_CURRENT     (1.0f)
#define BMS_EEPROM_ID            (0)
#define BMS_BATT_FAULT_VLT_THR   (5.0f)
#define BMS_BATT_FAULT_CURR_THR  (.005f)
#define BMS_CURR_AVR_FORG_FACT   (1.0f-(1.0f/8.0f))
#define BMS_RAPIDTEMP_CHECK_RATE (20.0f) /*Seconds*/
#define BMS_RAPIDTEMP_FAULT_THR  (7.0f)  /*Degrees C*/
#define BMS_RAPIDTEMP_CLEAR_THR  (4.0f)  /*Degrees C*/

/*---------------------------------------------------------------------------*
 * These values represent the calibration factors for setting the variable
 * f_gasGuage to its max and min value based on when the battery voltage
 * is charged above 'f_highVoltageThr' and discharges below 'f_lowVoltageThr'.
 *---------------------------------------------------------------------------*/
#define BMS_FULL_SOC_PERC  (.90f)
#define BMS_EMPTY_SOC_PERC (.10f)

#define HMI_WELCOME_SCREEN_DURATION (5)  /*Seconds*/
#define HMI_HOME_SCREEN_DURATION    (55) /*Seconds*/
#define HMI_BACKLIGHT_DURTION       (30) /*Seconds*/
#define HMI_BUTTON_PORT             (GPIO_PORTH)
#define HMI_BACKLIGHT_PIN           (PIN_0)
#define HMI_MENU_PIN                (PIN_1)
#define HMI_PWR_PIN                 (PIN_2)
#define HMI_PWR_BUTTON_DURATION     (5)

/*---------------------------------------------------------------------------*
 * Private Typedefs
 *---------------------------------------------------------------------------*/
typedef struct
{
   /*------------------------------------------------------------------------*
    * The serial number for the battery this particular BMS is attached to.
    *------------------------------------------------------------------------*/
   uint32_t i_battSerialNumber;
   /*------------------------------------------------------------------------*
    * The serial number for the particular BMS board this software is running
    * on.
    *------------------------------------------------------------------------*/
   uint32_t i_boardSerialNumber;
   /*------------------------------------------------------------------------*
    * The current software version number loaded on BMS board with serial
    * number 'i_boardSerialNumber'
    *------------------------------------------------------------------------*/
   //uint32_t i_softwareVersionNumber;
   /*------------------------------------------------------------------------*
    * The voltage, current, and temperature limits for this particular
    * battery.
    *------------------------------------------------------------------------*/
   t_bmsLimits t_limits;
   /*------------------------------------------------------------------------*
    * The voltage, current, and temperature calibration coefficients for this
    * particular battery management system.
    *------------------------------------------------------------------------*/
   t_bmsCal t_cal;
   /*------------------------------------------------------------------------*
    * This variable keeps track of the number of coulombs being added or
    * subtracted by a an external device connected to the battery. It is
    * represented by a Q16.15 number, and is the primary mechanism for
    * updating the estimated energy left in the battery 'f_gasGuage' by
    * dividing its floating-point value against the capacity of the
    * battery 'f_battAmpHours' converted to coulombs. The variable is
    * initialized to (f_battAmpHours*3600)*.9 when fully charged to the
    * 'f_highVoltageThr', and set to (f_battAmpHours*3600)*.1 when fully
    * discharged to 'f_lowVoltageThr'. It is continuously updated according to
    * the following:
    *    l_battLevelCoulombs -=
    * (int32_t)((t_bMeas.f_current*gt_bmsObjct.f_dt)*32767.0f)
    *------------------------------------------------------------------------*/
   int64_t l_battLevelCoulombs; /*Q48.15 number in Amps-seconds*/
   /*------------------------------------------------------------------------*
    * This variable keeps track on the total age of the system since leaving
    * factory calibration.
    *------------------------------------------------------------------------*/
   t_sysTime t_sysAge;
   /*------------------------------------------------------------------------*
    * This variable keeps track of the total number of Coulombs used to
    * charge the battery. It is represented by a Q48.15 number and provides a
    * means of gaging the age of the battery in terms of charge cycles- which
    * can be found by dividing the value by the total number of Coulombs for
    * a fully charged battery (f_battAmpHours*3600).
    *------------------------------------------------------------------------*/
   int64_t l_battAgeInCoulombs;

}t_bmsEeprom;

typedef struct
{
   /*------------------------------------------------------------------------*
    * Handle to the LCD driver.
    *------------------------------------------------------------------------*/
   t_DEVHANDLE t_lcdHndl;

   /*------------------------------------------------------------------------*
    * This variable stores the time at which the HMI first enters a new state.
    *------------------------------------------------------------------------*/
   t_sysTime t_startTime;

   /*------------------------------------------------------------------------*
    * If true, the user wants to turn on the back light.
    *------------------------------------------------------------------------*/
   bool b_bckLghtButtonEn;

   /*------------------------------------------------------------------------*
    * If true, the user wants to cycle through the menus.
    *------------------------------------------------------------------------*/
   bool b_menuButtonEn;

   /*------------------------------------------------------------------------*
    * If true, the user wants to power down the system.
    *------------------------------------------------------------------------*/
   bool b_pwrDownButtonEn;

   /*------------------------------------------------------------------------*
    * The last known cursor position.
    *------------------------------------------------------------------------*/
   t_cursPos t_cPos;

   /*------------------------------------------------------------------------*
    * When the battery gets lower than 12.5%, this variable controls the
    * blinking of the last bar in the fuel gage.
    *------------------------------------------------------------------------*/
   bool b_blinkControl;

   /*------------------------------------------------------------------------*
    * This variable marks the time at which the back light was turned on.
    *------------------------------------------------------------------------*/
   t_sysTime t_bckLghtStartTime;

   /*------------------------------------------------------------------------*
    * This variable marks the time at which the pwr down button was pressed.
    *------------------------------------------------------------------------*/
   t_sysTime t_pwrDwnBtnStartTime;

}t_hmiObjct;

typedef struct
{
   /*------------------------------------------------------------------------*
    * Resources that can be shared amongst multiple users (either a global
    * buffer or IO device) need to be protected against race conditions. We
    * use this semaphore for just that purpose.
    *------------------------------------------------------------------------*/
   t_SEMHANDLE t_mutex;
   /*------------------------------------------------------------------------*
    * This semaphore is set externally, and is used for transitioning the BMS
    * out of a particular "over-limit" state.
    *------------------------------------------------------------------------*/
   t_SEMHANDLE t_bmsLimitAck;
   /*------------------------------------------------------------------------*
    * Handle to the console thread
    *------------------------------------------------------------------------*/
   t_THRDHANDLE t_bmsThread;
   /*------------------------------------------------------------------------*
    * Handle to the LED driver.
    *------------------------------------------------------------------------*/
   t_DEVHANDLE t_signalHndl;
   /*------------------------------------------------------------------------*
    * Handle to the BMS driver.
    *------------------------------------------------------------------------*/
   t_DEVHANDLE t_bmsHndl;
   /*------------------------------------------------------------------------*
    * Handle to the state-machine object that defines how the BMS operates.
    *------------------------------------------------------------------------*/
   t_STMNHNDL t_stateMn;
   /*------------------------------------------------------------------------*
    * Handle to the EEPROM block where 't_eeprom' is being stored.
    *------------------------------------------------------------------------*/
   t_EEPROMHANDLE t_eepromHndl;
   /*------------------------------------------------------------------------*
    * The mailbox for incoming messages to the BMS.
    *------------------------------------------------------------------------*/
   t_MAILBOXHNDL t_bmsInMbxHndl;
   /*------------------------------------------------------------------------*
    * The mailbox for outgoing messages from the BMS.
    *------------------------------------------------------------------------*/
   t_MAILBOXHNDL t_bmsOutMbxHndl;
   /*------------------------------------------------------------------------*
    * The last known 'state' of the BMS.
    *------------------------------------------------------------------------*/
   t_bmsState t_prevState;
   /*------------------------------------------------------------------------*
    * The current 'state' of the BMS.
    *------------------------------------------------------------------------*/
   t_bmsState t_currState;
   /*------------------------------------------------------------------------*
    * The measured battery voltage, current, temperature, and flags signifying
    * the type of signal (AC or DC) connected to the charger.
    *------------------------------------------------------------------------*/
   t_bmsMeas t_bMeas;
   /*------------------------------------------------------------------------*
    * Variables that get stored in EEPROM.
    *------------------------------------------------------------------------*/
   volatile t_bmsEeprom t_eeprom;
   /*------------------------------------------------------------------------*
    * The last time the BMS thread ran.
    *------------------------------------------------------------------------*/
   t_sysTime t_prevTime;
   /*------------------------------------------------------------------------*
    * The current time step of BMS thread.
    *------------------------------------------------------------------------*/
   float f_dt;
   /*------------------------------------------------------------------------*
    * This variable stores the time at which the BMS first enters a new state.
    *------------------------------------------------------------------------*/
   t_sysTime t_startTime;
   /*------------------------------------------------------------------------*
    * This variable keeps track of the amount of energy left in the battery.
    * It represents a percentage of full, where at a battery voltage of
    * 'f_highVoltageThr' its 90% and at a voltage of 'f_lowVoltageThr' its
    * 10%. The variable is updated according to the following:
    *    f_gasGuage = (((float)l_battLevelCoulombs / 32768.0f)*100)/
    *    (f_battAmpHours*3600);
    *------------------------------------------------------------------------*/
   float f_gasGuage; /*Percentage of energy left*/
   /*------------------------------------------------------------------------*
    * This variable represents the battery 'BMS_FULL_SOC_PERC' capacity when
    * it reaches a voltage of 'f_highVoltageThr'.
    *------------------------------------------------------------------------*/
   int64_t l_batteryFullCoulombLevel;
   /*------------------------------------------------------------------------*
    * This variable represents the battery 'BMS_EMPTY_SOC_PERC' capacity when
    * it reaches a voltage of 'f_lowVoltageThr'.
    *------------------------------------------------------------------------*/
   int64_t l_batteryEmptyCoulombLevel;

//TODO store charge status variable here

   /*------------------------------------------------------------------------*
    * The fault status where,
    *              .---.---.---.---.---.---.---.---.
    *    c_fault = | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
    *              '---'---'---'---'---'---'---'---'
    *                            |   |   |   |   |
    *                            |   |   |   |   '-->BMS_FAULT_CURRENT
    *                            |   |   |   '------>BMS_FAULT_TEMP
    *                            |   |   '---------->BMS_FAULT_NO_BATT
    *                            |   '-------------->BMS_FAULT_CHARGE
    *                            '------------------>BMS_FAULT_RAPID_TEMP
    *------------------------------------------------------------------------*/
   uint8_t c_fault;

   /*------------------------------------------------------------------------*
    * Information pertaining to control of the LCD and user enabled buttons.
    *------------------------------------------------------------------------*/
   t_hmiObjct t_hmi;

}t_bmsObjct;

/*---------------------------------------------------------------------------*
 * Private Function Prototypes
 *---------------------------------------------------------------------------*/
static void usr_bmsFaultMonitor( void);
static bool usr_setBmsLimits( t_bmsLimits *pt_limits);
static void usr_bmsCollectMeas( void);
static void usr_storeEepromFactDefaults( void);
static void usr_getValuesFromEeprom( void);
static void usr_bmsChargeNom( int32_t i_stateData,
                              uint8_t c_prevState);
static void usr_bmsChargeDeep( int32_t i_stateData,
                               uint8_t c_prevState);
static void usr_bmsBoot( int32_t i_stateData,
                         uint8_t c_prevState);
static void usr_bmsNominal( int32_t i_stateData,
                            uint8_t c_prevState);
static void usr_bmsDeepDist( int32_t i_stateData,
                             uint8_t c_prevState);
static void usr_bmsFault( int32_t i_stateData,
                          uint8_t c_prevState);
static void usr_bmsPowerDown( int32_t i_stateData,
                              uint8_t c_prevState);
static void usr_bmsCheckForMessage( void);
static void usr_bms( t_parameters t_param,
                     t_arguments  t_args);
static void usr_bmsStateMachineInit( void);
static void usr_checkForButtonPress( void);
static void usr_clearButtonPress( void);
static void usr_hmiUpdateBcklghtBtn( void);
static void usr_hmiUpdateMenuBtn( void);
static void usr_hmiUpdatePwrDwnBtn( void);
static void usr_createFuelGage(void);
static void usr_createChargeIcon(void);
static void usr_displayFuelGauge( float *pf_fuel);
static void usr_lcdMainMenu( void);
static void usr_lcdShutdownMenu( int8_t c_time);
static void usr_lcdFaultMenu( void);
static void usr_displayChargeIcon( void);
static void usr_drawBattOutline( void);

/*---------------------------------------------------------------------------*
 * Private Global Variables
 *---------------------------------------------------------------------------*/
static t_bmsObjct gt_bmsObjct;

/*---------------------------------------------------------------------------*
 * Private Function Definitions
 *---------------------------------------------------------------------------*/
static void usr_createFuelGage(void)
{
   static t_custChar t_char;

   t_char.c_address = 0x0;
   t_char.ac_bitMap[0] = 0x00;
   t_char.ac_bitMap[1] = 0x00;
   t_char.ac_bitMap[2] = 0x00;
   t_char.ac_bitMap[3] = 0x00;
   t_char.ac_bitMap[4] = 0x00;
   t_char.ac_bitMap[5] = 0x00;
   t_char.ac_bitMap[6] = 0x00;
   t_char.ac_bitMap[7] = 0xFF;

   arb_ioctl( gt_bmsObjct.t_hmi.t_lcdHndl,
              LCD_STORE_CUST_CHAR,
              (uint32_t)((uint16_t)&t_char));

   t_char.c_address = 0x1;
   t_char.ac_bitMap[0] = 0xFF;
   t_char.ac_bitMap[1] = 0x00;
   t_char.ac_bitMap[2] = 0x00;
   t_char.ac_bitMap[3] = 0x00;
   t_char.ac_bitMap[4] = 0x00;
   t_char.ac_bitMap[5] = 0x00;
   t_char.ac_bitMap[6] = 0x00;
   t_char.ac_bitMap[7] = 0x00;

   arb_ioctl( gt_bmsObjct.t_hmi.t_lcdHndl,
              LCD_STORE_CUST_CHAR,
              (uint32_t)((uint16_t)&t_char));

   t_char.c_address = 0x2;
   t_char.ac_bitMap[0] = 0x10;
   t_char.ac_bitMap[1] = 0x10;
   t_char.ac_bitMap[2] = 0x10;
   t_char.ac_bitMap[3] = 0x10;
   t_char.ac_bitMap[4] = 0x10;
   t_char.ac_bitMap[5] = 0x10;
   t_char.ac_bitMap[6] = 0x10;
   t_char.ac_bitMap[7] = 0x10;

   arb_ioctl( gt_bmsObjct.t_hmi.t_lcdHndl,
              LCD_STORE_CUST_CHAR,
              (uint32_t)((uint16_t)&t_char));

   t_char.c_address = 0x3;
   t_char.ac_bitMap[0] = 0x10;
   t_char.ac_bitMap[1] = 0x10;
   t_char.ac_bitMap[2] = 0x18;
   t_char.ac_bitMap[3] = 0x18;
   t_char.ac_bitMap[4] = 0x18;
   t_char.ac_bitMap[5] = 0x18;
   t_char.ac_bitMap[6] = 0x10;
   t_char.ac_bitMap[7] = 0x10;

   arb_ioctl( gt_bmsObjct.t_hmi.t_lcdHndl,
              LCD_STORE_CUST_CHAR,
              (uint32_t)((uint16_t)&t_char));

   t_char.c_address = 0x7;
   t_char.ac_bitMap[0] = 0x01;
   t_char.ac_bitMap[1] = 0x01;
   t_char.ac_bitMap[2] = 0x01;
   t_char.ac_bitMap[3] = 0x01;
   t_char.ac_bitMap[4] = 0x01;
   t_char.ac_bitMap[5] = 0x01;
   t_char.ac_bitMap[6] = 0x01;
   t_char.ac_bitMap[7] = 0x01;

   arb_ioctl( gt_bmsObjct.t_hmi.t_lcdHndl,
              LCD_STORE_CUST_CHAR,
              (uint32_t)((uint16_t)&t_char));

}/*End usr_createFuelGage*/

static void usr_createChargeIcon(void)
{
   static t_custChar t_char;

   t_char.c_address = 0x4;
   t_char.ac_bitMap[0] = 0x10;
   t_char.ac_bitMap[1] = 0x10;
   t_char.ac_bitMap[2] = 0x10;
   t_char.ac_bitMap[3] = 0x10;
   t_char.ac_bitMap[4] = 0x10;
   t_char.ac_bitMap[5] = 0x11;
   t_char.ac_bitMap[6] = 0x13;
   t_char.ac_bitMap[7] = 0x14;

   arb_ioctl( gt_bmsObjct.t_hmi.t_lcdHndl,
              LCD_STORE_CUST_CHAR,
              (uint32_t)((uint16_t)&t_char));

   t_char.c_address = 0x5;
   t_char.ac_bitMap[0] = 0x01;
   t_char.ac_bitMap[1] = 0x03;
   t_char.ac_bitMap[2] = 0x07;
   t_char.ac_bitMap[3] = 0x0F;
   t_char.ac_bitMap[4] = 0x19;
   t_char.ac_bitMap[5] = 0x11;
   t_char.ac_bitMap[6] = 0x01;
   t_char.ac_bitMap[7] = 0x00;

   arb_ioctl( gt_bmsObjct.t_hmi.t_lcdHndl,
              LCD_STORE_CUST_CHAR,
              (uint32_t)((uint16_t)&t_char));

   t_char.c_address = 0x6;
   t_char.ac_bitMap[0] = 0x02;
   t_char.ac_bitMap[1] = 0x0C;
   t_char.ac_bitMap[2] = 0x18;
   t_char.ac_bitMap[3] = 0x10;
   t_char.ac_bitMap[4] = 0x00;
   t_char.ac_bitMap[5] = 0x00;
   t_char.ac_bitMap[6] = 0x00;
   t_char.ac_bitMap[7] = 0x00;

   arb_ioctl( gt_bmsObjct.t_hmi.t_lcdHndl,
              LCD_STORE_CUST_CHAR,
              (uint32_t)((uint16_t)&t_char));
}/*End usr_createChargeIcon*/

static void usr_hmiUpdateBcklghtBtn( void)
{
   t_sysTime t_currTime;
   int8_t c_elapsedTime;

   /*------------------------------------------------------------------------*
    * Has the user pressed the back light button?
    *------------------------------------------------------------------------*/
   if( utl_isGpioHigh( HMI_BUTTON_PORT, HMI_BACKLIGHT_PIN))
   {

      /*---------------------------------------------------------------------*
       * Record the time this event happened.
       *---------------------------------------------------------------------*/
      if( gt_bmsObjct.t_hmi.b_bckLghtButtonEn == false)
      {
         gt_bmsObjct.t_hmi.b_bckLghtButtonEn = true;
         gt_bmsObjct.t_hmi.t_bckLghtStartTime = arb_sysTimeNow();

         arb_ioctl( gt_bmsObjct.t_hmi.t_lcdHndl,
                    LCD_BACKLIGHT_ON,
                    0);
      }

   }/*End if( gt_bmsObjct.t_hmi.b_bckLghtButtonEn == true)*/
   else
   {
      gt_bmsObjct.t_hmi.b_bckLghtButtonEn = false;
   }

   if( gt_bmsObjct.t_hmi.t_bckLghtStartTime.l_ticks > 0)
   {
      t_currTime = arb_sysTimeNow();
      c_elapsedTime = (int8_t)t_currTime.c_sec -
      (int8_t)gt_bmsObjct.t_hmi.t_bckLghtStartTime.c_sec;
      if( c_elapsedTime < 0)
          c_elapsedTime += 60;

      if( c_elapsedTime >= HMI_BACKLIGHT_DURTION)
      {
         arb_ioctl( gt_bmsObjct.t_hmi.t_lcdHndl,
                    LCD_BACKLIGHT_OFF,
                    0);
         memset( (void *)&gt_bmsObjct.t_hmi.t_bckLghtStartTime,
                 0,
                 sizeof(gt_bmsObjct.t_hmi.t_bckLghtStartTime));

      }/*End if( c_elapsedTime >= HMI_BACKLIGHT_DURTION)*/

   }/*End if( gt_bmsObjct.t_hmi.t_bckLghtStartTime.l_ticks > 0)*/

}/*End usr_hmiUpdateBcklghtBtn*/

static void usr_hmiUpdateMenuBtn( void)
{

   /*------------------------------------------------------------------------*
    * Has the user pressed the back light button?
    *------------------------------------------------------------------------*/
   if( utl_isGpioHigh( HMI_BUTTON_PORT, HMI_MENU_PIN))
   {
      if( gt_bmsObjct.t_hmi.b_menuButtonEn == false)
      {
         gt_bmsObjct.t_hmi.b_menuButtonEn = true;
      }

   }
   else
   {
      gt_bmsObjct.t_hmi.b_menuButtonEn = false;
   }

}/*End usr_hmiUpdateMenuBtn*/

static void usr_hmiUpdatePwrDwnBtn( void)
{
   t_sysTime t_currTime;
   int8_t c_elapsedTime;
#if 0
   /*------------------------------------------------------------------------*
    * Has the user pressed the power down button?
    *------------------------------------------------------------------------*/
   if( utl_isGpioHigh( HMI_BUTTON_PORT, HMI_PWR_PIN))
   {

      /*---------------------------------------------------------------------*
       * Record the time this event first happened.
       *---------------------------------------------------------------------*/
      if( gt_bmsObjct.t_hmi.b_pwrDownButtonEn == false)
      {
         gt_bmsObjct.t_hmi.b_pwrDownButtonEn = true;
         gt_bmsObjct.t_hmi.t_pwrDwnBtnStartTime = arb_sysTimeNow();
      }

   }
   else
   {
      gt_bmsObjct.t_hmi.b_pwrDownButtonEn = false;
   }

   if( (gt_bmsObjct.t_hmi.t_pwrDwnBtnStartTime.l_ticks > 0) &&
       (gt_bmsObjct.t_hmi.b_pwrDownButtonEn == true))
   {
      t_currTime = arb_sysTimeNow();
      c_elapsedTime = (int8_t)t_currTime.c_sec -
      (int8_t)gt_bmsObjct.t_hmi.t_pwrDwnBtnStartTime.c_sec;
      if( c_elapsedTime < 0)
          c_elapsedTime += 60;

      if( c_elapsedTime >= HMI_PWR_BUTTON_DURATION)
      {
         /*------------------------------------------------------------------*
          * Transition to the power down state.
          *------------------------------------------------------------------*/
         utl_stMnChangeState( gt_bmsObjct.t_stateMn,
                              (uint8_t)BMS_POWER_DOWN,
                              0);
      }/*End if( c_elapsedTime >= HMI_PWR_BUTTON_DURATION)*/

   }/*End if( gt_bmsObjct.t_pwrDwnBtnStartTime.l_ticks > 0)*/
#endif
}/*End usr_hmiUpdatePwrDwnBtn*/

static void usr_checkForButtonPress( void)
{
   usr_hmiUpdateMenuBtn();
   usr_hmiUpdateBcklghtBtn();
   usr_hmiUpdatePwrDwnBtn();

}/*End usr_checkForButtonPress*/

static void usr_clearButtonPress( void)
{
   gt_bmsObjct.t_hmi.b_menuButtonEn    = false;
   gt_bmsObjct.t_hmi.b_bckLghtButtonEn = false;

}/*End usr_clearButtonPress*/

static void usr_drawBattOutline( void)
{
  t_cursPos t_cur;

   t_cur.c_row = 2;
   t_cur.c_col = 13;
   arb_ioctl( gt_bmsObjct.t_hmi.t_lcdHndl,
              LCD_CURSOR_SET_POS,
              (uint32_t)((uint16_t)&t_cur));

   arb_ioctl( gt_bmsObjct.t_hmi.t_lcdHndl,
              LCD_WRITE_BUILT_IN_FONT,
              0x07);

   t_cur.c_row = 1;
   t_cur.c_col = 14;
   arb_ioctl( gt_bmsObjct.t_hmi.t_lcdHndl,
              LCD_CURSOR_SET_POS,
              (uint32_t)((uint16_t)&t_cur));
   arb_ioctl( gt_bmsObjct.t_hmi.t_lcdHndl,
              LCD_WRITE_BUILT_IN_FONT,
              0x00);

   t_cur.c_row = 1;
   t_cur.c_col = 15;
   arb_ioctl( gt_bmsObjct.t_hmi.t_lcdHndl,
              LCD_CURSOR_SET_POS,
              (uint32_t)((uint16_t)&t_cur));
   arb_ioctl( gt_bmsObjct.t_hmi.t_lcdHndl,
              LCD_WRITE_BUILT_IN_FONT,
              0x00);

   t_cur.c_row = 1;
   t_cur.c_col = 16;
   arb_ioctl( gt_bmsObjct.t_hmi.t_lcdHndl,
              LCD_CURSOR_SET_POS,
              (uint32_t)((uint16_t)&t_cur));
   arb_ioctl( gt_bmsObjct.t_hmi.t_lcdHndl,
              LCD_WRITE_BUILT_IN_FONT,
              0x00);

   t_cur.c_row = 1;
   t_cur.c_col = 17;
   arb_ioctl( gt_bmsObjct.t_hmi.t_lcdHndl,
              LCD_CURSOR_SET_POS,
              (uint32_t)((uint16_t)&t_cur));
   arb_ioctl( gt_bmsObjct.t_hmi.t_lcdHndl,
              LCD_WRITE_BUILT_IN_FONT,
              0x00);

   t_cur.c_row = 2;
   t_cur.c_col = 18;
   arb_ioctl( gt_bmsObjct.t_hmi.t_lcdHndl,
              LCD_CURSOR_SET_POS,
              (uint32_t)((uint16_t)&t_cur));
   arb_ioctl( gt_bmsObjct.t_hmi.t_lcdHndl,
              LCD_WRITE_BUILT_IN_FONT,
              0x03);

   //t_cur.c_row = 2;
   //t_cur.c_col = 14;
   //arb_ioctl( gt_bmsObjct.t_hmi.t_lcdHndl,
   //           LCD_CURSOR_SET_POS,
   //           (uint32_t)((uint16_t)&t_cur));
   //arb_ioctl( gt_bmsObjct.t_hmi.t_lcdHndl,
   //           LCD_WRITE_BUILT_IN_FONT,
   //           0x02);

   t_cur.c_row = 3;
   t_cur.c_col = 14;
   arb_ioctl( gt_bmsObjct.t_hmi.t_lcdHndl,
              LCD_CURSOR_SET_POS,
              (uint32_t)((uint16_t)&t_cur));
   arb_ioctl( gt_bmsObjct.t_hmi.t_lcdHndl,
              LCD_WRITE_BUILT_IN_FONT,
              0x01);

   t_cur.c_row = 3;
   t_cur.c_col = 15;
   arb_ioctl( gt_bmsObjct.t_hmi.t_lcdHndl,
              LCD_CURSOR_SET_POS,
              (uint32_t)((uint16_t)&t_cur));
   arb_ioctl( gt_bmsObjct.t_hmi.t_lcdHndl,
              LCD_WRITE_BUILT_IN_FONT,
              0x01);

   t_cur.c_row = 3;
   t_cur.c_col = 16;
   arb_ioctl( gt_bmsObjct.t_hmi.t_lcdHndl,
              LCD_CURSOR_SET_POS,
              (uint32_t)((uint16_t)&t_cur));
   arb_ioctl( gt_bmsObjct.t_hmi.t_lcdHndl,
              LCD_WRITE_BUILT_IN_FONT,
              0x01);

   t_cur.c_row = 3;
   t_cur.c_col = 17;
   arb_ioctl( gt_bmsObjct.t_hmi.t_lcdHndl,
              LCD_CURSOR_SET_POS,
              (uint32_t)((uint16_t)&t_cur));
   arb_ioctl( gt_bmsObjct.t_hmi.t_lcdHndl,
              LCD_WRITE_BUILT_IN_FONT,
              0x01);

}/*End usr_drawBattOutline*/

static void usr_displayChargeIcon( void)
{
   t_cursPos t_cur;
   
   usr_drawBattOutline();
  
   t_cur.c_row = 2;
   t_cur.c_col = 14;
   arb_ioctl( gt_bmsObjct.t_hmi.t_lcdHndl,
               LCD_CURSOR_SET_POS,
               (uint32_t)((uint16_t)&t_cur));
   arb_ioctl( gt_bmsObjct.t_hmi.t_lcdHndl,
               LCD_WRITE_BUILT_IN_FONT,
               0x4);
   t_cur.c_row = 2;
   t_cur.c_col = 15;
   arb_ioctl( gt_bmsObjct.t_hmi.t_lcdHndl,
               LCD_CURSOR_SET_POS,
               (uint32_t)((uint16_t)&t_cur));
   arb_ioctl( gt_bmsObjct.t_hmi.t_lcdHndl,
               LCD_WRITE_BUILT_IN_FONT,
               0x5);
   t_cur.c_row = 2;
   t_cur.c_col = 16;
   arb_ioctl( gt_bmsObjct.t_hmi.t_lcdHndl,
               LCD_CURSOR_SET_POS,
               (uint32_t)((uint16_t)&t_cur));
   arb_ioctl( gt_bmsObjct.t_hmi.t_lcdHndl,
               LCD_WRITE_BUILT_IN_FONT,
               0x5);
   t_cur.c_row = 2;
   t_cur.c_col = 17;
   arb_ioctl( gt_bmsObjct.t_hmi.t_lcdHndl,
               LCD_CURSOR_SET_POS,
               (uint32_t)((uint16_t)&t_cur));
   arb_ioctl( gt_bmsObjct.t_hmi.t_lcdHndl,
               LCD_WRITE_BUILT_IN_FONT,
               0x6);

}/*End usr_displayChargeIcon*/

static void usr_displayFuelGauge( float *pf_fuel)
{
   t_cursPos t_cur;

   usr_drawBattOutline();

   if( *pf_fuel > 75)
   {

      t_cur.c_row = 2;
      t_cur.c_col = 14;
      arb_ioctl( gt_bmsObjct.t_hmi.t_lcdHndl,
                 LCD_CURSOR_SET_POS,
                 (uint32_t)((uint16_t)&t_cur));
      arb_ioctl( gt_bmsObjct.t_hmi.t_lcdHndl,
                 LCD_WRITE_BUILT_IN_FONT,
                 0xFF);
      t_cur.c_row = 2;
      t_cur.c_col = 15;
      arb_ioctl( gt_bmsObjct.t_hmi.t_lcdHndl,
                 LCD_CURSOR_SET_POS,
                 (uint32_t)((uint16_t)&t_cur));
      arb_ioctl( gt_bmsObjct.t_hmi.t_lcdHndl,
                 LCD_WRITE_BUILT_IN_FONT,
                 0xFF);
      t_cur.c_row = 2;
      t_cur.c_col = 16;
      arb_ioctl( gt_bmsObjct.t_hmi.t_lcdHndl,
                 LCD_CURSOR_SET_POS,
                 (uint32_t)((uint16_t)&t_cur));
      arb_ioctl( gt_bmsObjct.t_hmi.t_lcdHndl,
                 LCD_WRITE_BUILT_IN_FONT,
                 0xFF);
      t_cur.c_row = 2;
      t_cur.c_col = 17;
      arb_ioctl( gt_bmsObjct.t_hmi.t_lcdHndl,
                 LCD_CURSOR_SET_POS,
                 (uint32_t)((uint16_t)&t_cur));
      arb_ioctl( gt_bmsObjct.t_hmi.t_lcdHndl,
                 LCD_WRITE_BUILT_IN_FONT,
                 0xFF);
   }
   else if( *pf_fuel > 50)
   {

      t_cur.c_row = 2;
      t_cur.c_col = 14;
      arb_ioctl( gt_bmsObjct.t_hmi.t_lcdHndl,
                 LCD_CURSOR_SET_POS,
                 (uint32_t)((uint16_t)&t_cur));
      arb_ioctl( gt_bmsObjct.t_hmi.t_lcdHndl,
                 LCD_WRITE_BUILT_IN_FONT,
                 0xFF);
      t_cur.c_row = 2;
      t_cur.c_col = 15;
      arb_ioctl( gt_bmsObjct.t_hmi.t_lcdHndl,
                 LCD_CURSOR_SET_POS,
                 (uint32_t)((uint16_t)&t_cur));
      arb_ioctl( gt_bmsObjct.t_hmi.t_lcdHndl,
                 LCD_WRITE_BUILT_IN_FONT,
                 0xFF);
      t_cur.c_row = 2;
      t_cur.c_col = 16;
      arb_ioctl( gt_bmsObjct.t_hmi.t_lcdHndl,
                 LCD_CURSOR_SET_POS,
                 (uint32_t)((uint16_t)&t_cur));
      arb_ioctl( gt_bmsObjct.t_hmi.t_lcdHndl,
                 LCD_WRITE_BUILT_IN_FONT,
                 0xFF);
      t_cur.c_row = 2;
      t_cur.c_col = 17;
      arb_ioctl( gt_bmsObjct.t_hmi.t_lcdHndl,
                 LCD_CURSOR_SET_POS,
                 (uint32_t)((uint16_t)&t_cur));
      arb_ioctl( gt_bmsObjct.t_hmi.t_lcdHndl,
                 LCD_WRITE_BUILT_IN_FONT,
                 ' ');
   }
   else if( *pf_fuel > 25)
   {

      t_cur.c_row = 2;
      t_cur.c_col = 14;
      arb_ioctl( gt_bmsObjct.t_hmi.t_lcdHndl,
                 LCD_CURSOR_SET_POS,
                 (uint32_t)((uint16_t)&t_cur));
      arb_ioctl( gt_bmsObjct.t_hmi.t_lcdHndl,
                 LCD_WRITE_BUILT_IN_FONT,
                 0xFF);
      t_cur.c_row = 2;
      t_cur.c_col = 15;
      arb_ioctl( gt_bmsObjct.t_hmi.t_lcdHndl,
                 LCD_CURSOR_SET_POS,
                 (uint32_t)((uint16_t)&t_cur));
      arb_ioctl( gt_bmsObjct.t_hmi.t_lcdHndl,
                 LCD_WRITE_BUILT_IN_FONT,
                 0xFF);
      t_cur.c_row = 2;
      t_cur.c_col = 16;
      arb_ioctl( gt_bmsObjct.t_hmi.t_lcdHndl,
                 LCD_CURSOR_SET_POS,
                 (uint32_t)((uint16_t)&t_cur));
      arb_ioctl( gt_bmsObjct.t_hmi.t_lcdHndl,
                 LCD_WRITE_BUILT_IN_FONT,
                 ' ');
      t_cur.c_row = 2;
      t_cur.c_col = 17;
      arb_ioctl( gt_bmsObjct.t_hmi.t_lcdHndl,
                 LCD_CURSOR_SET_POS,
                 (uint32_t)((uint16_t)&t_cur));
      arb_ioctl( gt_bmsObjct.t_hmi.t_lcdHndl,
                 LCD_WRITE_BUILT_IN_FONT,
                 ' ');
   }
   else if( *pf_fuel > 12.5)
   {


      t_cur.c_row = 2;
      t_cur.c_col = 14;
      arb_ioctl( gt_bmsObjct.t_hmi.t_lcdHndl,
                 LCD_CURSOR_SET_POS,
                 (uint32_t)((uint16_t)&t_cur));
      arb_ioctl( gt_bmsObjct.t_hmi.t_lcdHndl,
                 LCD_WRITE_BUILT_IN_FONT,
                 0xFF);
      t_cur.c_row = 2;
      t_cur.c_col = 15;
      arb_ioctl( gt_bmsObjct.t_hmi.t_lcdHndl,
                 LCD_CURSOR_SET_POS,
                 (uint32_t)((uint16_t)&t_cur));
      arb_ioctl( gt_bmsObjct.t_hmi.t_lcdHndl,
                 LCD_WRITE_BUILT_IN_FONT,
                 ' ');
      t_cur.c_row = 2;
      t_cur.c_col = 16;
      arb_ioctl( gt_bmsObjct.t_hmi.t_lcdHndl,
                 LCD_CURSOR_SET_POS,
                 (uint32_t)((uint16_t)&t_cur));
      arb_ioctl( gt_bmsObjct.t_hmi.t_lcdHndl,
                 LCD_WRITE_BUILT_IN_FONT,
                 ' ');
      t_cur.c_row = 2;
      t_cur.c_col = 17;
      arb_ioctl( gt_bmsObjct.t_hmi.t_lcdHndl,
                 LCD_CURSOR_SET_POS,
                 (uint32_t)((uint16_t)&t_cur));
      arb_ioctl( gt_bmsObjct.t_hmi.t_lcdHndl,
                 LCD_WRITE_BUILT_IN_FONT,
                 ' ');
   }
   else /*Blink the last bar when battery very low...*/
   {

      if( gt_bmsObjct.t_hmi.b_blinkControl == false)
      {
         gt_bmsObjct.t_hmi.b_blinkControl = true;
         t_cur.c_row = 2;
         t_cur.c_col = 14;
         arb_ioctl( gt_bmsObjct.t_hmi.t_lcdHndl,
                    LCD_CURSOR_SET_POS,
                    (uint32_t)((uint16_t)&t_cur));
         arb_ioctl( gt_bmsObjct.t_hmi.t_lcdHndl,
                    LCD_WRITE_BUILT_IN_FONT,
                    0xFF); /*Black box*/
      }
      else
      {
         gt_bmsObjct.t_hmi.b_blinkControl = false;
         t_cur.c_row = 2;
         t_cur.c_col = 14;
         arb_ioctl( gt_bmsObjct.t_hmi.t_lcdHndl,
                    LCD_CURSOR_SET_POS,
                    (uint32_t)((uint16_t)&t_cur));
         arb_ioctl( gt_bmsObjct.t_hmi.t_lcdHndl,
                    LCD_WRITE_BUILT_IN_FONT,
                    ' ');
      }

      t_cur.c_row = 2;
      t_cur.c_col = 15;
      arb_ioctl( gt_bmsObjct.t_hmi.t_lcdHndl,
                 LCD_CURSOR_SET_POS,
                 (uint32_t)((uint16_t)&t_cur));
      arb_ioctl( gt_bmsObjct.t_hmi.t_lcdHndl,
                 LCD_WRITE_BUILT_IN_FONT,
                 ' ');
      t_cur.c_row = 2;
      t_cur.c_col = 16;
      arb_ioctl( gt_bmsObjct.t_hmi.t_lcdHndl,
                 LCD_CURSOR_SET_POS,
                 (uint32_t)((uint16_t)&t_cur));
      arb_ioctl( gt_bmsObjct.t_hmi.t_lcdHndl,
                 LCD_WRITE_BUILT_IN_FONT,
                 ' ');
      t_cur.c_row = 2;
      t_cur.c_col = 17;
      arb_ioctl( gt_bmsObjct.t_hmi.t_lcdHndl,
                 LCD_CURSOR_SET_POS,
                 (uint32_t)((uint16_t)&t_cur));
      arb_ioctl( gt_bmsObjct.t_hmi.t_lcdHndl,
                 LCD_WRITE_BUILT_IN_FONT,
                 ' ');
   }

}/*End usr_displayFuelGauge*/

static void usr_lcdMainMenu( void)
{
   t_cursPos t_cur;
   uint8_t ac_buff[50];
   uint16_t s_size;
   float af_bmsMeas[4];
   t_bmsMeasStatus t_status;

   t_status = usr_getBmsMeas();

   /*------------------------------------------------------------------------*
    * Display the banner...
    *------------------------------------------------------------------------*/
   t_cur.c_row = 0;
   t_cur.c_col = 1;

   arb_ioctl( gt_bmsObjct.t_hmi.t_lcdHndl,
              LCD_CURSOR_SET_POS,
              (uint32_t)((uint16_t)&t_cur));

   s_size = sprintf_P( (char *)ac_buff, PSTR("www.watts-rock.com"));
   arb_write( gt_bmsObjct.t_hmi.t_lcdHndl,
              (int8_t *)ac_buff,
              s_size);

   af_bmsMeas[0] = t_status.t_meas.f_volts;
   af_bmsMeas[1] = t_status.t_meas.f_current;
   af_bmsMeas[2] = t_status.t_meas.f_temp;
   af_bmsMeas[3] = t_status.f_gasGuage;

   /*------------------------------------------------------------------------*
    * Round to the first decimal place...
    *------------------------------------------------------------------------*/
   af_bmsMeas[0] = roundf( af_bmsMeas[0]*10.0f) / 10.0f;
   /*------------------------------------------------------------------------*
    * Round to the second decimal place...
    *------------------------------------------------------------------------*/
   af_bmsMeas[1] = roundf( af_bmsMeas[1]*100.0f) / 100.0f;
   /*------------------------------------------------------------------------*
    * Round to the nearest integer and change from Celsius to Fahrenheit
    *------------------------------------------------------------------------*/
   af_bmsMeas[2] = (roundf( ((af_bmsMeas[2])*9.0f/5.0f) + 32.0f));
   /*------------------------------------------------------------------------*
    * Round to the nearest integer
    *------------------------------------------------------------------------*/
   af_bmsMeas[3] = roundf( af_bmsMeas[3]);

   /*------------------------------------------------------------------------*
    * Display the voltage, current, and temperature.
    *------------------------------------------------------------------------*/
   t_cur.c_row = 1;
   t_cur.c_col = 1;

   arb_ioctl( gt_bmsObjct.t_hmi.t_lcdHndl,
              LCD_CURSOR_SET_POS,
              (uint32_t)((uint16_t)&t_cur));

   s_size = sprintf_P( (char *)ac_buff, PSTR("%2.2fV"), af_bmsMeas[0]);

   arb_write( gt_bmsObjct.t_hmi.t_lcdHndl,
              (int8_t *)ac_buff,
              s_size);

   t_cur.c_row = 2;
   t_cur.c_col = 1;

   arb_ioctl( gt_bmsObjct.t_hmi.t_lcdHndl,
              LCD_CURSOR_SET_POS,
              (uint32_t)((uint16_t)&t_cur));

   s_size = sprintf_P( (char *)ac_buff, PSTR("%2.3fA"), af_bmsMeas[1]);

   arb_write( gt_bmsObjct.t_hmi.t_lcdHndl,
              (int8_t *)ac_buff,
              s_size);

   t_cur.c_row = 3;
   t_cur.c_col = 1;

   arb_ioctl( gt_bmsObjct.t_hmi.t_lcdHndl,
              LCD_CURSOR_SET_POS,
              (uint32_t)((uint16_t)&t_cur));

   s_size = sprintf_P( (char *)ac_buff, PSTR("%3.0fF"), af_bmsMeas[2]);

   arb_write( gt_bmsObjct.t_hmi.t_lcdHndl,
              (int8_t *)ac_buff,
              s_size);
#if 0

   /*------------------------------------------------------------------------*
    * Has the charger been connected?
    *------------------------------------------------------------------------*/
   if( t_status.t_state == BMS_MPP_CHARGE)
   {
      t_cur.c_row = 3;
      t_cur.c_col = 4;

      arb_ioctl( gt_bmsObjct.t_hmi.t_lcdHndl,
                 LCD_CURSOR_SET_POS,
                 (uint32_t)((uint16_t)&t_cur));

      s_size = sprintf_P( (char *)ac_buff, PSTR(" MPP Track"));

      arb_write( gt_bmsObjct.t_hmi.t_lcdHndl,
                 (int8_t *)ac_buff,
                 s_size);
   }
   else if( t_status.t_state == BMS_CC_CHARGE)
   {
      t_cur.c_row = 3;
      t_cur.c_col = 4;

      arb_ioctl( gt_bmsObjct.t_hmi.t_lcdHndl,
      LCD_CURSOR_SET_POS,
      (uint32_t)((uint16_t)&t_cur));

      s_size = sprintf_P( (char *)ac_buff, PSTR(" CC Charge"));

      arb_write( gt_bmsObjct.t_hmi.t_lcdHndl,
      (int8_t *)ac_buff,
      s_size);
   }
   else if( t_status.t_state == BMS_CV_CHARGE)
   {
      t_cur.c_row = 3;
      t_cur.c_col = 4;

      arb_ioctl( gt_bmsObjct.t_hmi.t_lcdHndl,
      LCD_CURSOR_SET_POS,
      (uint32_t)((uint16_t)&t_cur));

      s_size = sprintf_P( (char *)ac_buff, PSTR(" CV Charge"));

      arb_write( gt_bmsObjct.t_hmi.t_lcdHndl,
      (int8_t *)ac_buff,
      s_size);
   }
   else
   {
      t_cur.c_row = 3;
      t_cur.c_col = 4;

      arb_ioctl( gt_bmsObjct.t_hmi.t_lcdHndl,
                 LCD_CURSOR_SET_POS,
                 (uint32_t)((uint16_t)&t_cur));

      s_size = sprintf_P( (char *)ac_buff, PSTR("__________"));

      arb_write( gt_bmsObjct.t_hmi.t_lcdHndl,
                 (int8_t *)ac_buff,
                 s_size);
   }
#endif

   /*------------------------------------------------------------------------*
    * Display the fuel gage.
    *------------------------------------------------------------------------*/
   usr_displayFuelGauge( &af_bmsMeas[3]);

}/*End usr_lcdMainMenu*/

static void usr_lcdShutdownMenu( int8_t c_time)
{

   t_cursPos t_cur;
   uint8_t ac_buff[50];
   uint16_t s_size;

   t_cur.c_row = 1;
   t_cur.c_col = 0;

   arb_ioctl( gt_bmsObjct.t_hmi.t_lcdHndl,
              LCD_CURSOR_SET_POS,
              (uint32_t)((uint16_t)&t_cur));

   s_size = sprintf_P( (char *)ac_buff, PSTR("Power off in %2d sec"), c_time);

   arb_write( gt_bmsObjct.t_hmi.t_lcdHndl,
              (int8_t *)ac_buff,
              s_size);

}/*End usr_lcdShutdownMenu*/

static void usr_lcdFaultMenu( void)
{
   t_cursPos t_cur;
   uint8_t ac_buff[50];
   uint16_t s_size;

   if( gt_bmsObjct.c_fault & BMS_FAULT_RAPID_TEMP)
   {
      t_cur.c_row = 1;
      t_cur.c_col = 2;

      arb_ioctl( gt_bmsObjct.t_hmi.t_lcdHndl,
      LCD_CURSOR_SET_POS,
      (uint32_t)((uint16_t)&t_cur));

      s_size = sprintf_P( (char *)ac_buff, PSTR("RAPID TEMP LIMIT!"));
      arb_write( gt_bmsObjct.t_hmi.t_lcdHndl,
      (int8_t *)ac_buff,
      s_size);

      t_cur.c_row = 2;
      t_cur.c_col = 1;

      arb_ioctl( gt_bmsObjct.t_hmi.t_lcdHndl,
      LCD_CURSOR_SET_POS,
      (uint32_t)((uint16_t)&t_cur));

      s_size = sprintf_P( (char *)ac_buff, PSTR("Output turned off"));
      arb_write( gt_bmsObjct.t_hmi.t_lcdHndl,
      (int8_t *)ac_buff,
      s_size);
   }/*End if( gt_bmsObjct.c_fault & BMS_FAULT_RAPID_TEMP())*/
   else if( gt_bmsObjct.c_fault & BMS_FAULT_TEMP)
   {

      t_cur.c_row = 1;
      t_cur.c_col = 2;

      arb_ioctl( gt_bmsObjct.t_hmi.t_lcdHndl,
                 LCD_CURSOR_SET_POS,
                 (uint32_t)((uint16_t)&t_cur));

      s_size = sprintf_P( (char *)ac_buff, PSTR("TEMP LIMIT!"));
      arb_write( gt_bmsObjct.t_hmi.t_lcdHndl,
                 (int8_t *)ac_buff,
                 s_size);

      t_cur.c_row = 2;
      t_cur.c_col = 1;

      arb_ioctl( gt_bmsObjct.t_hmi.t_lcdHndl,
                 LCD_CURSOR_SET_POS,
                 (uint32_t)((uint16_t)&t_cur));

      s_size = sprintf_P( (char *)ac_buff, PSTR("Output turned off"));
      arb_write( gt_bmsObjct.t_hmi.t_lcdHndl,
                 (int8_t *)ac_buff,
                 s_size);

   }
   else if( gt_bmsObjct.c_fault & BMS_FAULT_CURRENT)
   {
      t_cur.c_row = 1;
      t_cur.c_col = 2;

      arb_ioctl( gt_bmsObjct.t_hmi.t_lcdHndl,
                 LCD_CURSOR_SET_POS,
                 (uint32_t)((uint16_t)&t_cur));

      s_size = sprintf_P( (char *)ac_buff, PSTR("CURRENT LIMIT!"));
      arb_write( gt_bmsObjct.t_hmi.t_lcdHndl,
                 (int8_t *)ac_buff,
                 s_size);

      t_cur.c_row = 2;
      t_cur.c_col = 1;

      arb_ioctl( gt_bmsObjct.t_hmi.t_lcdHndl,
                 LCD_CURSOR_SET_POS,
                 (uint32_t)((uint16_t)&t_cur));

      s_size = sprintf_P( (char *)ac_buff, PSTR("Output turned off"));
      arb_write( gt_bmsObjct.t_hmi.t_lcdHndl,
                 (int8_t *)ac_buff,
                 s_size);

      t_cur.c_row = 3;
      t_cur.c_col = 0;

      arb_ioctl( gt_bmsObjct.t_hmi.t_lcdHndl,
                 LCD_CURSOR_SET_POS,
                 (uint32_t)((uint16_t)&t_cur));

      s_size = sprintf_P( (char *)ac_buff, PSTR("Press btn to reset"));
      arb_write( gt_bmsObjct.t_hmi.t_lcdHndl,
                 (int8_t *)ac_buff,
                 s_size);

   }
   else if( gt_bmsObjct.c_fault & BMS_FAULT_NO_BATT)
   {
      t_cur.c_row = 1;
      t_cur.c_col = 2;

      arb_ioctl( gt_bmsObjct.t_hmi.t_lcdHndl,
      LCD_CURSOR_SET_POS,
      (uint32_t)((uint16_t)&t_cur));

      s_size = sprintf_P( (char *)ac_buff, PSTR("NO BATT DETECTED!"));
      arb_write( gt_bmsObjct.t_hmi.t_lcdHndl,
      (int8_t *)ac_buff,
      s_size);

      t_cur.c_row = 2;
      t_cur.c_col = 1;

      arb_ioctl( gt_bmsObjct.t_hmi.t_lcdHndl,
      LCD_CURSOR_SET_POS,
      (uint32_t)((uint16_t)&t_cur));

      s_size = sprintf_P( (char *)ac_buff, PSTR("Output turned off"));
      arb_write( gt_bmsObjct.t_hmi.t_lcdHndl,
      (int8_t *)ac_buff,
      s_size);
   }
   else if( gt_bmsObjct.c_fault & BMS_FAULT_CHARGE)
   {
      t_cur.c_row = 1;
      t_cur.c_col = 2;

      arb_ioctl( gt_bmsObjct.t_hmi.t_lcdHndl,
      LCD_CURSOR_SET_POS,
      (uint32_t)((uint16_t)&t_cur));

      s_size = sprintf_P( (char *)ac_buff, PSTR("CHARGE FAULT!"));
      arb_write( gt_bmsObjct.t_hmi.t_lcdHndl,
      (int8_t *)ac_buff,
      s_size);

      t_cur.c_row = 2;
      t_cur.c_col = 1;

      arb_ioctl( gt_bmsObjct.t_hmi.t_lcdHndl,
      LCD_CURSOR_SET_POS,
      (uint32_t)((uint16_t)&t_cur));

      s_size = sprintf_P( (char *)ac_buff, PSTR("Output turned off"));
      arb_write( gt_bmsObjct.t_hmi.t_lcdHndl,
      (int8_t *)ac_buff,
      s_size);
      t_cur.c_row = 3;
      t_cur.c_col = 0;

      arb_ioctl( gt_bmsObjct.t_hmi.t_lcdHndl,
                 LCD_CURSOR_SET_POS,
                 (uint32_t)((uint16_t)&t_cur));

      s_size = sprintf_P( (char *)ac_buff, PSTR("Press btn to reset"));
      arb_write( gt_bmsObjct.t_hmi.t_lcdHndl,
                 (int8_t *)ac_buff,
                 s_size);

   }

}/*End usr_lcdFaultMenu*/

static void usr_bmsFaultMonitor( void)
{
   static float f_prevTemp = -1000.0f;
   static float f_timer = 0.0f;

   f_timer = f_timer + gt_bmsObjct.f_dt;
   if( f_timer >= BMS_RAPIDTEMP_CHECK_RATE)
   {
      /*---------------------------------------------------------------------*
       * If the temperature has rose rapidly, throw a fault.
       *---------------------------------------------------------------------*/
      if( ((gt_bmsObjct.t_bMeas.f_temp - f_prevTemp) 
            >= BMS_RAPIDTEMP_FAULT_THR) && (f_prevTemp > -1000.0f))
      {
         gt_bmsObjct.c_fault |= BMS_FAULT_RAPID_TEMP;
      }
      else if( ((gt_bmsObjct.t_bMeas.f_temp - f_prevTemp)
      <= BMS_RAPIDTEMP_CLEAR_THR))
      {
         gt_bmsObjct.c_fault &= ~BMS_FAULT_RAPID_TEMP;
      }

      f_prevTemp = gt_bmsObjct.t_bMeas.f_temp;
      f_timer = 0.0f;

   }/*End if( f_timer >= BMS_RAPIDTEMP_CHECK_RATE)*/

   /*------------------------------------------------------------------------*
    * Is there a battery connected?
    *------------------------------------------------------------------------*/
   if( gt_bmsObjct.t_bMeas.f_volts <= BMS_BATT_FAULT_VLT_THR)
   {
      gt_bmsObjct.c_fault |= BMS_FAULT_NO_BATT;
   }
   else
      gt_bmsObjct.c_fault &= ~BMS_FAULT_NO_BATT;

   /*------------------------------------------------------------------------*
    * Is the charger running for a period of time? If so, monitor the current
    * to make sure its negative, if its not then the charge port might be open
    * circuit. If open, a fault will be thrown and won't be reset until an
    * external user acknowledges the issue.
    *------------------------------------------------------------------------*/
   //if( (gt_bmsObjct.t_charge.t_state != CHRG_IDLE) &&
   //    (gt_bmsObjct.t_charge.f_chargeTime > 10.0f))
   //{
   //   if( gt_bmsObjct.t_bMeas.f_current > 0)
   //      gt_bmsObjct.c_fault |= BMS_FAULT_CHARGE;

   //}

   /*------------------------------------------------------------------------*
    * Is the system pulling too much current? If yes, the fault condition
    * is only cleared once user acknowledges the problem.
    *------------------------------------------------------------------------*/
   if( fabs(gt_bmsObjct.t_bMeas.f_avrCurrent) >= gt_bmsObjct.t_eeprom.t_limits.
   f_highCurrThr)
   {
      gt_bmsObjct.c_fault |= BMS_FAULT_CURRENT;
   }

   /*------------------------------------------------------------------------*
    * Is the system within temperature limits?
    *------------------------------------------------------------------------*/
   if( gt_bmsObjct.t_bMeas.f_temp >= gt_bmsObjct.t_eeprom.t_limits.
   f_highTempThr)
   {
      gt_bmsObjct.c_fault |= BMS_FAULT_TEMP;
   }
   else if( gt_bmsObjct.t_bMeas.f_temp < (gt_bmsObjct.t_eeprom.t_limits.
            f_highTempThr - gt_bmsObjct.t_eeprom.t_limits.f_tempHyst))
   {
      gt_bmsObjct.c_fault &= ~BMS_FAULT_TEMP;
   }

}/*End usr_bmsFaultMonitor*/

t_bmsLimits *usr_getBmsLimits( void)
{
   return (t_bmsLimits *)&gt_bmsObjct.t_eeprom.t_limits;
}/*End usr_getBmsLimits*/

/*---------------------------------------------------------------------------*
 * Configures the absolute battery voltage, current, and temperature limits.
 *---------------------------------------------------------------------------*/
static bool usr_setBmsLimits( t_bmsLimits *pt_limits)
{
   bool b_valid = true;
   t_eepromError t_err;

   if( (pt_limits->f_lowVoltageThr < BMS_MIN_LOW_VOLTAGE_THR) ||
       (pt_limits->f_lowVoltageThr > BMS_MAX_LOW_VOLTAGE_THR) ||
       (pt_limits->f_highVoltageThr < BMS_MIN_HIGH_VOLTAGE_THR) ||
       (pt_limits->f_highVoltageThr > BMS_MAX_HIGH_VOLTAGE_THR))
   {
      b_valid = false;
   }
   else if( (pt_limits->f_highCurrThr < BMS_MIN_HIGH_CURRENT_THR) ||
            (pt_limits->f_highCurrThr > BMS_MAX_HIGH_CURRENT_THR))
   {
      b_valid = false;
   }
   else if( (pt_limits->f_highTempThr < BMS_MIN_HIGH_TEMP_THR) ||
            (pt_limits->f_highTempThr > BMS_MAX_HIGH_TEMP_THR))
   {
      b_valid = false;
   }
   else if( (pt_limits->f_battAmpHours < BMS_MIN_BATT_AMP_HR) ||
            (pt_limits->f_battAmpHours > BMS_MAX_BATT_AMP_HR))
   {
      b_valid = false;
   }
   else if( (pt_limits->f_maxChargeCurrent < BMS_MIN_CHRG_CURRENT) ||
   (pt_limits->f_maxChargeCurrent > BMS_MAX_CHRG_CURRENT))
   {
      b_valid = false;
   }

   if( b_valid == true)
   {
      gt_bmsObjct.t_eeprom.t_limits.f_lowVoltageThr  =
      pt_limits->f_lowVoltageThr; /*Volts*/
      gt_bmsObjct.t_eeprom.t_limits.f_highVoltageThr =
      pt_limits->f_highVoltageThr; /*Volts*/
      gt_bmsObjct.t_eeprom.t_limits.f_highCurrThr    =
      pt_limits->f_highCurrThr; /*Amps*/
      gt_bmsObjct.t_eeprom.t_limits.f_highTempThr    =
      pt_limits->f_highTempThr; /*degrees C*/
      gt_bmsObjct.t_eeprom.t_limits.f_battAmpHours   =
      pt_limits->f_battAmpHours; /*Amp-hrs*/
      gt_bmsObjct.t_eeprom.t_limits.f_maxChargeCurrent   =
      pt_limits->f_maxChargeCurrent; /*Amps*/

      /*---------------------------------------------------------------------*
       * Configure the high and low Coulomb count limits...
       *---------------------------------------------------------------------*/
      gt_bmsObjct.l_batteryEmptyCoulombLevel = (int64_t)(gt_bmsObjct.t_eeprom.
      t_limits.f_battAmpHours*3600.0f*BMS_EMPTY_SOC_PERC*32767.0f);

      gt_bmsObjct.l_batteryFullCoulombLevel = (int64_t)(gt_bmsObjct.t_eeprom.
      t_limits.f_battAmpHours*3600.0f*BMS_FULL_SOC_PERC*32767.0f);

      /*---------------------------------------------------------------------*
       * Store the new settings in EEPROM...
       *---------------------------------------------------------------------*/
      t_err = utl_writeEeprom( gt_bmsObjct.t_eepromHndl,
                               (uint8_t *)&gt_bmsObjct.t_eeprom,
                               (uint16_t)sizeof( gt_bmsObjct.t_eeprom));

      if( t_err < 0)
         exit(0);
   }

   return b_valid;

}/*End usr_setBmsLimits*/

static void usr_bmsCollectMeas( void)
{
   char ac_buff[200];
   t_bmsMeas t_meas;
   t_sysTime t_time;
   uint16_t s_size;
   float f_chrgCoulombs;

   t_time = arb_sysTimeNow();

   /*------------------------------------------------------------------------*
    * Keep track of how long its been since the last time this function was
    * called - this is the update rate of 'usr_bms'. When first coming out of
    * reset, gt_bmsObjct.t_prevTime is not valid so don't calculate.
    *------------------------------------------------------------------------*/
   if( gt_bmsObjct.t_prevTime.l_ticks > 0)
   {
      gt_bmsObjct.f_dt = ((float)t_time.l_ticks -
      (float)gt_bmsObjct.t_prevTime.l_ticks) / ARB_SYS_TIMER_TICK_RATE;
   }
   else
      gt_bmsObjct.f_dt = 0;

   gt_bmsObjct.t_prevTime = t_time;

   /*------------------------------------------------------------------------*
    * Collect new voltage, current, and temperature measurements, as well
    * as monitoring for the charger being connected.
    *------------------------------------------------------------------------*/
//utl_gpioOn( GPIO_PORTC,
//            PIN_6);

   arb_ioctl( gt_bmsObjct.t_bmsHndl,
              BMS_GET_MEAS,
              (uint32_t)((uint16_t)&t_meas));

//utl_gpioOff( GPIO_PORTC,
//             PIN_6);

   /*------------------------------------------------------------------------*
    * Store the results...
    *------------------------------------------------------------------------*/
   if( t_meas.f_pacCurrent < 0.0f)
      t_meas.f_pacCurrent = 0.0f;
      
   gt_bmsObjct.t_bMeas.f_current    = t_meas.f_current;
   gt_bmsObjct.t_bMeas.f_pacCurrent = t_meas.f_pacCurrent;
   gt_bmsObjct.t_bMeas.f_temp       = t_meas.f_temp;
   gt_bmsObjct.t_bMeas.f_volts      = t_meas.f_volts;

   gt_bmsObjct.t_bMeas.f_avrCurrent =
   gt_bmsObjct.t_bMeas.f_avrCurrent*BMS_CURR_AVR_FORG_FACT +
   (1.0f - BMS_CURR_AVR_FORG_FACT)*t_meas.f_current;

   /*------------------------------------------------------------------------*
    * If current is negative - device is charging, keep track- over the
    * lifetime of the battery - the total number of charge coulombs in order
    * to estimate battery age.
    *------------------------------------------------------------------------*/
//TODO, check charger status in this statement as well.
   if( (gt_bmsObjct.t_bMeas.f_current < 0))
   {

      f_chrgCoulombs = fabs(gt_bmsObjct.t_bMeas.f_current)*gt_bmsObjct.f_dt;
      gt_bmsObjct.t_eeprom.l_battAgeInCoulombs += ((int64_t)(f_chrgCoulombs*
      32767.0f));
   }/*End if( gt_bmsObjct.t_bMeas.f_current < 0)*/

   /*------------------------------------------------------------------------*
    * Update the capacity estimate...
    *------------------------------------------------------------------------*/
   gt_bmsObjct.t_eeprom.l_battLevelCoulombs -=
   ((int64_t)(t_meas.f_current*gt_bmsObjct.f_dt*32767.0f));

   /*------------------------------------------------------------------------*
    * Update the battery level in Coulombs as long as the battery voltage is
    * within the low and high voltage thresholds.
    *------------------------------------------------------------------------*/
   if( (gt_bmsObjct.t_bMeas.f_volts <= gt_bmsObjct.t_eeprom.t_limits.
       f_lowVoltageThr) || (gt_bmsObjct.t_eeprom.l_battLevelCoulombs <
       gt_bmsObjct.l_batteryEmptyCoulombLevel))
   {
      /*---------------------------------------------------------------------*
       * Set to 10% capacity left...
       *---------------------------------------------------------------------*/
      gt_bmsObjct.t_eeprom.l_battLevelCoulombs = gt_bmsObjct.
      l_batteryEmptyCoulombLevel;
   }
   else if( (gt_bmsObjct.t_bMeas.f_volts >= gt_bmsObjct.t_eeprom.t_limits.
   f_highVoltageThr) || (gt_bmsObjct.t_eeprom.l_battLevelCoulombs >
   gt_bmsObjct.l_batteryFullCoulombLevel))
   {
      /*---------------------------------------------------------------------*
       * Set to 90% capacity left...
       *---------------------------------------------------------------------*/
      gt_bmsObjct.t_eeprom.l_battLevelCoulombs = gt_bmsObjct.
      l_batteryFullCoulombLevel;
   }

   gt_bmsObjct.f_gasGuage = (((float)gt_bmsObjct.t_eeprom.l_battLevelCoulombs /
   32768.0f)*100.0f) / (gt_bmsObjct.t_eeprom.t_limits.f_battAmpHours*3600.0f);

   /*------------------------------------------------------------------------*
    * If enabled, send debug to the console...
    *------------------------------------------------------------------------*/
   s_size = sprintf(( char *)ac_buff, "Volts= %2.2f Amps= %2.5f PAC Amps= %2.2f Gas Gage= %3.4f Temp C= %3.2f\r",
   (double)gt_bmsObjct.t_bMeas.f_volts,
   (double)gt_bmsObjct.t_bMeas.f_avrCurrent,
   (double)gt_bmsObjct.t_bMeas.f_pacCurrent,
   (double)gt_bmsObjct.f_gasGuage,
   (double)gt_bmsObjct.t_bMeas.f_temp);
   arb_printf( PRINTF_DBG_LOW | PRINTF_DBG_SHOW_TIME,
               (const char *)ac_buff);

}/*End usr_bmsCollectMeas*/

static void usr_storeEepromFactDefaults( void)
{
   t_eepromError t_err;

   /*---------------------------------------------------------------------------*
    * Initialize the default board and battery version numbers
    *---------------------------------------------------------------------------*/
   gt_bmsObjct.t_eeprom.i_battSerialNumber = BMS_DEFAULT_BATTERY_SERIAL_NUMBER;
   gt_bmsObjct.t_eeprom.i_boardSerialNumber = BMS_DEFAULT_BOARD_SERIAL_NUMBER;
   //gt_bmsObjct.t_eeprom.i_softwareVersionNumber = (uint32_t)BMS_SOFTWARE_VERSION_NUMBER;

   /*---------------------------------------------------------------------------*
    * Initialize the volt and current meter calibration factors.
    *---------------------------------------------------------------------------*/
   gt_bmsObjct.t_eeprom.t_cal.f_calVltMtrBias    = -0.00037f; /*Volts*/
   gt_bmsObjct.t_eeprom.t_cal.f_calVltMtrRDivide = 40.93322f;
   gt_bmsObjct.t_eeprom.t_cal.f_calCurMtrBias    = 0.0032f;   /*Volts*/
   gt_bmsObjct.t_eeprom.t_cal.f_calRsense        = .033f;     /*Ohms*/
   gt_bmsObjct.t_eeprom.t_cal.f_calTempMtrBias   = -0.00037f; /*Volts*/
   gt_bmsObjct.t_eeprom.t_cal.f_calPacCurMtrBias = -0.00317f; /*Volts*/
   gt_bmsObjct.t_eeprom.t_cal.f_calPacRsense     = 0.038f;    /*Ohms*/

   /*---------------------------------------------------------------------------*
    * Initialize the battery limits to the BMS 'birth date'.
    *---------------------------------------------------------------------------*/
   gt_bmsObjct.t_eeprom.t_limits.f_lowVoltageThr    = 41.0f; /*Volts*/
   gt_bmsObjct.t_eeprom.t_limits.f_highVoltageThr   = 52.5f; /*Volts*/
   gt_bmsObjct.t_eeprom.t_limits.f_highCurrThr      = 30.0f; /*Amps*/
   gt_bmsObjct.t_eeprom.t_limits.f_highTempThr      = 90.0f; /*degrees C*/
   gt_bmsObjct.t_eeprom.t_limits.f_tempHyst         = 5.0f;  /*degrees C*/
   gt_bmsObjct.t_eeprom.t_limits.f_voltHyst         = 1.0f;  /*Volts*/
   gt_bmsObjct.t_eeprom.t_limits.f_battAmpHours     = 20.0f; /*amp-hr*/
   gt_bmsObjct.t_eeprom.t_limits.f_maxChargeCurrent = 3.0f;  /*Amps*/

   gt_bmsObjct.t_eeprom.l_battLevelCoulombs = (int64_t)(gt_bmsObjct.t_eeprom.
   t_limits.f_battAmpHours*3600.0f*BMS_FULL_SOC_PERC*32767.0f);
   gt_bmsObjct.t_eeprom.l_battAgeInCoulombs = 0;

   /*---------------------------------------------------------------------------*
    * Store the factory defaults back into the eeprom.
    *---------------------------------------------------------------------------*/
   t_err = utl_writeEeprom( gt_bmsObjct.t_eepromHndl,
                            (uint8_t *)&gt_bmsObjct.t_eeprom,
                            (uint16_t)sizeof( gt_bmsObjct.t_eeprom));

   if( t_err < 0)
      exit(0);

}/*End usr_storeEepromFactDefaults*/

static void usr_getValuesFromEeprom( void)
{
   t_eepromError t_err;

   t_err = utl_readEeprom( gt_bmsObjct.t_eepromHndl,
                           (uint8_t *)&gt_bmsObjct.t_eeprom,
                           (uint16_t)sizeof( gt_bmsObjct.t_eeprom));

   if( t_err < 0)
   {
      arb_printf( PRINTF_DBG_HIGH,
                  "Failed to load EEPROM\n\r");
      exit(0);
   }

}/*End usr_getValuesFromEeprom*/

static void usr_bmsBoot( int32_t i_stateData,
                         uint8_t c_prevState)
{
   t_stmnError t_stErr;
   t_sysTime t_currTime;
   int8_t c_elapsedTime;
   t_cursPos t_cur;
   uint8_t ac_buff[30];
   uint16_t s_size;

   if( c_prevState != BMS_BOOT)
   {

      /*---------------------------------------------------------------------*
       * Remember the time at which we entered this state...
       *---------------------------------------------------------------------*/
      gt_bmsObjct.t_startTime = arb_sysTimeNow();

      /*---------------------------------------------------------------------*
       * Make sure the motherboard stays on by sending a logic one to the
       * voltage regulator controller.
       *---------------------------------------------------------------------*/
      arb_ioctl( gt_bmsObjct.t_bmsHndl,
                 BMS_PWR_CNTRL,
                 BMS_DEVICE_ON);

//TODO shutoff charger here

      arb_ioctl( gt_bmsObjct.t_bmsHndl,
                 BMS_FAN_CNTRL,
                 BMS_DEVICE_OFF);

      arb_ioctl( gt_bmsObjct.t_bmsHndl,
                 BMS_PACK_CNTRL,
                 BMS_DEVICE_OFF);

      arb_ioctl( gt_bmsObjct.t_signalHndl,
                 ALL_LEDS_OFF,
                 0);

      arb_ioctl( gt_bmsObjct.t_hmi.t_lcdHndl,
                 LCD_CLEAR,
                 0);

      arb_ioctl( gt_bmsObjct.t_hmi.t_lcdHndl,
                 LCD_CURSOR_HOME,
                 0);

      arb_ioctl( gt_bmsObjct.t_hmi.t_lcdHndl,
                 LCD_DISPLAY_ON,
                 0);

      arb_ioctl( gt_bmsObjct.t_hmi.t_lcdHndl,
                 LCD_BACKLIGHT_ON,
                 0);

      gt_bmsObjct.t_hmi.b_menuButtonEn    = false;
      gt_bmsObjct.t_hmi.b_bckLghtButtonEn = false;
      gt_bmsObjct.t_hmi.b_pwrDownButtonEn = false;

      t_cur.c_row = 1;
      t_cur.c_col = 6;
      arb_ioctl( gt_bmsObjct.t_hmi.t_lcdHndl,
                 LCD_CURSOR_SET_POS,
                 (uint32_t)((uint16_t)&t_cur));

      s_size = sprintf_P( (char *)ac_buff, PSTR("Welcome"));
      arb_write( gt_bmsObjct.t_hmi.t_lcdHndl,
                 (int8_t *)ac_buff,
                 s_size);

      /*---------------------------------------------------------------------*
       * Store the battery icon.
       *---------------------------------------------------------------------*/
      usr_createFuelGage();

      usr_createChargeIcon();

      gt_bmsObjct.t_currState = BMS_BOOT;

      /*---------------------------------------------------------------------*
       * Keep track of where we came from...
       *---------------------------------------------------------------------*/
      gt_bmsObjct.t_prevState = (t_bmsState)c_prevState;

   }/*End if( c_prevState != BMS_BOOT)*/

   t_currTime = arb_sysTimeNow();
   c_elapsedTime = (int8_t)t_currTime.c_sec -
   (int8_t)gt_bmsObjct.t_startTime.c_sec;
   if( c_elapsedTime < 0)
      c_elapsedTime += 60;

   /*------------------------------------------------------------------------*
    * Determine the next state to transition to...
    *------------------------------------------------------------------------*/
   if( c_elapsedTime >= HMI_WELCOME_SCREEN_DURATION)
   {
      arb_ioctl( gt_bmsObjct.t_hmi.t_lcdHndl,
                 LCD_CLEAR,
                 0);

      arb_ioctl( gt_bmsObjct.t_hmi.t_lcdHndl,
                 LCD_BACKLIGHT_OFF,
                 0);

      if( gt_bmsObjct.c_fault)
      {
         t_stErr = utl_stMnChangeState( gt_bmsObjct.t_stateMn,
                                        (uint8_t)BMS_FAULT,
                                        0);
      }
      else if( gt_bmsObjct.t_bMeas.f_volts <= gt_bmsObjct.t_eeprom.t_limits.
      f_lowVoltageThr)
      {
         t_stErr = utl_stMnChangeState( gt_bmsObjct.t_stateMn,
                                        (uint8_t)BMS_DEEP_DIST,
                                        0);
      }
      else if( gt_bmsObjct.t_bMeas.f_volts > gt_bmsObjct.t_eeprom.t_limits.
      f_lowVoltageThr)
      {
         t_stErr = utl_stMnChangeState( gt_bmsObjct.t_stateMn,
                                        (uint8_t)BMS_NOMINAL,
                                        0);
      }
   }/*End if( c_elapsedTime >= HMI_WELCOME_SCREEN_DURATION)*/

}/*End usr_bmsBoot*/

static void usr_bmsNominal( int32_t i_stateData,
                            uint8_t c_prevState)
{
   t_stmnError t_stErr;
   bool b_chrgDet;
   static bool b_fanEn;

   if( c_prevState != BMS_NOMINAL)
   {
      arb_ioctl( gt_bmsObjct.t_signalHndl,
                 ALL_LEDS_OFF,
                 0);

//TODO turn charger on here...it will charge if it can

      arb_ioctl( gt_bmsObjct.t_bmsHndl,
                 BMS_FAN_CNTRL,
                 BMS_DEVICE_OFF);

      arb_ioctl( gt_bmsObjct.t_bmsHndl,
                 BMS_PACK_CNTRL,
                 BMS_DEVICE_ON);

      arb_ioctl( gt_bmsObjct.t_hmi.t_lcdHndl,
                 LCD_CLEAR,
                 0);

      arb_ioctl( gt_bmsObjct.t_hmi.t_lcdHndl,
                 LCD_CURSOR_HOME,
                 0);

      arb_ioctl( gt_bmsObjct.t_hmi.t_lcdHndl,
                 LCD_BACKLIGHT_OFF,
                 0);

      gt_bmsObjct.t_currState = BMS_NOMINAL;

      /*---------------------------------------------------------------------*
       * Keep track of where we came from...
       *---------------------------------------------------------------------*/
      gt_bmsObjct.t_prevState = (t_bmsState)c_prevState;

      /*---------------------------------------------------------------------*
       * Display indication that the BMS is in the nominal state.
       *---------------------------------------------------------------------*/
      arb_ioctl( gt_bmsObjct.t_signalHndl,
                 GREEN_LED_ON,
                 0);

      b_fanEn = false;

   }/*End if( c_prevState != BMS_NOMINAL)*/

   if( (fabs(gt_bmsObjct.t_bMeas.f_current) >=
       (gt_bmsObjct.t_eeprom.t_limits.f_highCurrThr*.3f)) ||
       (gt_bmsObjct.t_bMeas.f_temp >= 
       (gt_bmsObjct.t_eeprom.t_limits.f_highTempThr*.5)))
   {
      if( b_fanEn == false)
      {
         arb_ioctl( gt_bmsObjct.t_bmsHndl,
                    BMS_FAN_CNTRL,
                    BMS_DEVICE_ON);
         b_fanEn = true;
      }
   }
   else if( (gt_bmsObjct.t_bMeas.f_temp <= 
            ((gt_bmsObjct.t_eeprom.t_limits.f_highTempThr*.5) - 
              gt_bmsObjct.t_eeprom.t_limits.f_tempHyst)) &&
            (fabs(gt_bmsObjct.t_bMeas.f_current) <=
            ((gt_bmsObjct.t_eeprom.t_limits.f_highCurrThr*.3f)-1.0f)))
   {
      if( b_fanEn == true)
      {
         arb_ioctl( gt_bmsObjct.t_bmsHndl,
                    BMS_FAN_CNTRL,
                    BMS_DEVICE_OFF);
         b_fanEn = false;
      }
   }
   
//TODO, update with status from the charger
b_chrgDet = false;//gt_bmsObjct.t_bMeas.b_acChargeDet |
//gt_bmsObjct.t_bMeas.b_dcChargeDet;

   /*------------------------------------------------------------------------*
    * Display LCD main menu...
    *------------------------------------------------------------------------*/
   usr_lcdMainMenu();

   /*------------------------------------------------------------------------*
    * Determine the next state to transition to...
    *------------------------------------------------------------------------*/
   if( gt_bmsObjct.c_fault)
   {
      t_stErr = utl_stMnChangeState( gt_bmsObjct.t_stateMn,
                                     (uint8_t)BMS_FAULT,
                                     0);
   }
   else if( b_chrgDet == true)
   {
      t_stErr = utl_stMnChangeState( gt_bmsObjct.t_stateMn,
                                     (uint8_t)BMS_CHARGE_NOM,
                                     0);
   }
   else if( gt_bmsObjct.t_bMeas.f_volts <= gt_bmsObjct.t_eeprom.t_limits.
   f_lowVoltageThr)
   {
      t_stErr = utl_stMnChangeState( gt_bmsObjct.t_stateMn,
                                     (uint8_t)BMS_DEEP_DIST,
                                     0);
   }
  
//TODO DO something with charger here

}/*End usr_bmsNominal*/

static void usr_bmsChargeNom( int32_t i_stateData,
                              uint8_t c_prevState)
{
   t_stmnError t_stErr;
   bool b_chrgDet;

   if( c_prevState != BMS_CHARGE_NOM)
   {
      arb_ioctl( gt_bmsObjct.t_signalHndl,
                 ALL_LEDS_OFF,
                 0);

//TODO turn charger on here...it will charge if it can

      arb_ioctl( gt_bmsObjct.t_bmsHndl,
                 BMS_FAN_CNTRL,
                 BMS_DEVICE_ON);

      arb_ioctl( gt_bmsObjct.t_bmsHndl,
                 BMS_PACK_CNTRL,
                 BMS_DEVICE_ON);

      arb_ioctl( gt_bmsObjct.t_hmi.t_lcdHndl,
                 LCD_CLEAR,
                 0);

      arb_ioctl( gt_bmsObjct.t_hmi.t_lcdHndl,
                 LCD_CURSOR_HOME,
                 0);

      arb_ioctl( gt_bmsObjct.t_hmi.t_lcdHndl,
                 LCD_BACKLIGHT_OFF,
                 0);

      gt_bmsObjct.t_currState = BMS_CHARGE_NOM;

      /*---------------------------------------------------------------------*
       * Keep track of where we came from...
       *---------------------------------------------------------------------*/
      gt_bmsObjct.t_prevState = (t_bmsState)c_prevState;

      /*---------------------------------------------------------------------*
       * Display indication that the BMS is in the nominal state.
       *---------------------------------------------------------------------*/
      arb_ioctl( gt_bmsObjct.t_signalHndl,
                 YELLOW_LED_ON,
                 0);

   }/*End if( c_prevState != BMS_CHARGE_NOM)*/

//TODO, update with status from the charger
b_chrgDet = false;//gt_bmsObjct.t_bMeas.b_acChargeDet |
//gt_bmsObjct.t_bMeas.b_dcChargeDet;

   /*------------------------------------------------------------------------*
    * Display LCD main menu...
    *------------------------------------------------------------------------*/
   usr_lcdMainMenu();

   /*------------------------------------------------------------------------*
    * Determine the next state to transition to...
    *------------------------------------------------------------------------*/
   if( gt_bmsObjct.c_fault)
   {
      t_stErr = utl_stMnChangeState( gt_bmsObjct.t_stateMn,
                                     (uint8_t)BMS_FAULT,
                                     0);
   }
   else if( b_chrgDet == false)
   {
      t_stErr = utl_stMnChangeState( gt_bmsObjct.t_stateMn,
                                     (uint8_t)BMS_NOMINAL,
                                     0);
   }

}/*End usr_bmsChargeNom*/

static void usr_bmsDeepDist( int32_t i_stateData,
                             uint8_t c_prevState)
{
   static int8_t c_deepDisTimeRem;
   static bool b_fanEn;
   t_sysTime t_currTime;
   int8_t c_elapsedTime;
   t_stmnError t_stErr;
   bool b_chrgDet;

   if( c_prevState != BMS_DEEP_DIST)
   {
      /*---------------------------------------------------------------------*
       * Remember the time at which we entered this state...
       *---------------------------------------------------------------------*/
      gt_bmsObjct.t_startTime = arb_sysTimeNow();

      arb_ioctl( gt_bmsObjct.t_signalHndl,
                 ALL_LEDS_OFF,
                 0);

//TODO turn charger on here...it will charge if it can

      /*---------------------------------------------------------------------*
       * Shut the battery output off in order to protect the cells from
       * damage.
       *---------------------------------------------------------------------*/
      arb_ioctl( gt_bmsObjct.t_bmsHndl,
                 BMS_FAN_CNTRL,
                 BMS_DEVICE_OFF);

      arb_ioctl( gt_bmsObjct.t_bmsHndl,
                 BMS_PACK_CNTRL,
                 BMS_DEVICE_OFF);

      arb_ioctl( gt_bmsObjct.t_hmi.t_lcdHndl,
                 LCD_CLEAR,
                 0);

      arb_ioctl( gt_bmsObjct.t_hmi.t_lcdHndl,
                 LCD_CURSOR_HOME,
                 0);

      arb_ioctl( gt_bmsObjct.t_hmi.t_lcdHndl,
                 LCD_BACKLIGHT_ON,
                 0);

      gt_bmsObjct.t_currState = BMS_DEEP_DIST;

      /*---------------------------------------------------------------------*
       * Keep track of where we came from...
       *---------------------------------------------------------------------*/
      gt_bmsObjct.t_prevState = (t_bmsState)c_prevState;

      c_deepDisTimeRem = BMS_DEEP_DIST_DURATION;
      b_fanEn = false;

   }/*End if( c_prevState != BMS_DEEP_DIST)*/

   /*------------------------------------------------------------------------*
    * Keep track of the time left until power down...
    *------------------------------------------------------------------------*/
   if( c_deepDisTimeRem > 0)
   {
      t_currTime = arb_sysTimeNow();
      c_elapsedTime = (int8_t)t_currTime.c_sec - (int8_t)gt_bmsObjct.t_startTime.
      c_sec;
      if( c_elapsedTime < 0)
         c_elapsedTime += 60;
      c_deepDisTimeRem = BMS_DEEP_DIST_DURATION - c_elapsedTime;
   }

   if( (fabs(gt_bmsObjct.t_bMeas.f_current) >=
       (gt_bmsObjct.t_eeprom.t_limits.f_highCurrThr*.3f)) ||
       (gt_bmsObjct.t_bMeas.f_temp >= 
       (gt_bmsObjct.t_eeprom.t_limits.f_highTempThr*.5)))
   {
      if( b_fanEn == false)
      {
         arb_ioctl( gt_bmsObjct.t_bmsHndl,
                    BMS_FAN_CNTRL,
                    BMS_DEVICE_ON);
         b_fanEn = true;
      }
   }
   else if( (gt_bmsObjct.t_bMeas.f_temp <= 
            ((gt_bmsObjct.t_eeprom.t_limits.f_highTempThr*.5) - 
              gt_bmsObjct.t_eeprom.t_limits.f_tempHyst)) &&
            (fabs(gt_bmsObjct.t_bMeas.f_current) <=
            ((gt_bmsObjct.t_eeprom.t_limits.f_highCurrThr*.3f)-1.0f)))
   {
      if( b_fanEn == true)
      {
         arb_ioctl( gt_bmsObjct.t_bmsHndl,
                    BMS_FAN_CNTRL,
                    BMS_DEVICE_OFF);
         b_fanEn = false;
      }
   }

//TODO update with status from charger
b_chrgDet = false;//gt_bmsObjct.t_bMeas.b_acChargeDet |
//gt_bmsObjct.t_bMeas.b_dcChargeDet;

   usr_lcdShutdownMenu( c_deepDisTimeRem);

   if( c_deepDisTimeRem == 0)
   {
      t_stErr = utl_stMnChangeState( gt_bmsObjct.t_stateMn,
                                     (uint8_t)BMS_POWER_DOWN,
                                     0);
   }
   else if( b_chrgDet == true)
   {
      t_stErr = utl_stMnChangeState( gt_bmsObjct.t_stateMn,
                                     (uint8_t)BMS_CHARGE_DEEP,
                                     0);
   }

}/*End usr_bmsDeepDist*/

static void usr_bmsChargeDeep( int32_t i_stateData,
                               uint8_t c_prevState)
{
   t_stmnError t_stErr;
   bool b_chrgDet;

   if( c_prevState != BMS_CHARGE_DEEP)
   {
      arb_ioctl( gt_bmsObjct.t_signalHndl,
                 ALL_LEDS_OFF,
                 0);

//TODO turn charger on here...it will charge if it can

      arb_ioctl( gt_bmsObjct.t_bmsHndl,
                 BMS_FAN_CNTRL,
                 BMS_DEVICE_ON);

      arb_ioctl( gt_bmsObjct.t_bmsHndl,
                 BMS_PACK_CNTRL,
                 BMS_DEVICE_OFF);

      arb_ioctl( gt_bmsObjct.t_hmi.t_lcdHndl,
                 LCD_CLEAR,
                 0);

      arb_ioctl( gt_bmsObjct.t_hmi.t_lcdHndl,
                 LCD_CURSOR_HOME,
                 0);

      arb_ioctl( gt_bmsObjct.t_hmi.t_lcdHndl,
                 LCD_BACKLIGHT_OFF,
                 0);

      gt_bmsObjct.t_currState = BMS_CHARGE_DEEP;

      /*---------------------------------------------------------------------*
       * Keep track of where we came from...
       *---------------------------------------------------------------------*/
      gt_bmsObjct.t_prevState = (t_bmsState)c_prevState;

      /*---------------------------------------------------------------------*
       * Display indication that the BMS is in the nominal state.
       *---------------------------------------------------------------------*/
      arb_ioctl( gt_bmsObjct.t_signalHndl,
                 YELLOW_LED_ON,
                 0);

   }/*End if( c_prevState != BMS_CHARGE_DEEP)*/

//TODO, update with status from the charger
b_chrgDet = false;//gt_bmsObjct.t_bMeas.b_acChargeDet |
//gt_bmsObjct.t_bMeas.b_dcChargeDet;

   /*------------------------------------------------------------------------*
    * Display LCD main menu...
    *------------------------------------------------------------------------*/
   usr_lcdMainMenu();

   /*------------------------------------------------------------------------*
    * Determine the next state to transition to...
    *------------------------------------------------------------------------*/
   if( gt_bmsObjct.c_fault)
   {
      t_stErr = utl_stMnChangeState( gt_bmsObjct.t_stateMn,
                                     (uint8_t)BMS_FAULT,
                                     0);
   }
   else if( (gt_bmsObjct.t_bMeas.f_volts > gt_bmsObjct.t_eeprom.t_limits.
   f_lowVoltageThr) && (b_chrgDet == true))
   {
      t_stErr = utl_stMnChangeState( gt_bmsObjct.t_stateMn,
                                       (uint8_t)BMS_CHARGE_NOM,
                                       0);
   }
   else if( b_chrgDet == false)
   {
      t_stErr = utl_stMnChangeState( gt_bmsObjct.t_stateMn,
                                     (uint8_t)BMS_DEEP_DIST,
                                     0);
   }
   
}/*End usr_bmsChargeDeep*/

static void usr_bmsFault( int32_t i_stateData,
                          uint8_t c_prevState)
{

   if( c_prevState != BMS_FAULT)
   {
      if( (gt_bmsObjct.c_fault & BMS_FAULT_TEMP) ||
          (gt_bmsObjct.c_fault & BMS_FAULT_RAPID_TEMP))
      {
         arb_ioctl( gt_bmsObjct.t_bmsHndl,
                    BMS_FAN_CNTRL,
                    BMS_DEVICE_ON);
      }

//TODO, shutoff charger

      arb_ioctl( gt_bmsObjct.t_bmsHndl,
                 BMS_PACK_CNTRL,
                 BMS_DEVICE_OFF);

      arb_ioctl( gt_bmsObjct.t_signalHndl,
                 ALL_LEDS_OFF,
                 0);

      arb_ioctl( gt_bmsObjct.t_hmi.t_lcdHndl,
                 LCD_CLEAR,
                 0);

      arb_ioctl( gt_bmsObjct.t_hmi.t_lcdHndl,
                 LCD_CURSOR_HOME,
                 0);

      arb_ioctl( gt_bmsObjct.t_hmi.t_lcdHndl,
                 LCD_BACKLIGHT_ON,
                 0);

      gt_bmsObjct.t_currState = BMS_FAULT;

      /*---------------------------------------------------------------------*
       * Keep track of where we came from...
       *---------------------------------------------------------------------*/
      gt_bmsObjct.t_prevState = (t_bmsState)c_prevState;

   }/*End if( c_prevState != BMS_FAULT)*/

   usr_lcdFaultMenu();

   /*------------------------------------------------------------------------*
    * Has the user acknowledged the over-current condition by pressing a
    * button?
    *------------------------------------------------------------------------*/
   if( (gt_bmsObjct.t_hmi.b_bckLghtButtonEn == true) ||
       (gt_bmsObjct.t_hmi.b_menuButtonEn == true) ||
       (gt_bmsObjct.t_hmi.b_pwrDownButtonEn == true))
   {
      if( gt_bmsObjct.c_fault & BMS_FAULT_CURRENT)
      {
         if( fabs(gt_bmsObjct.t_bMeas.f_current) < gt_bmsObjct.t_eeprom.t_limits.
             f_highCurrThr)
         {
            gt_bmsObjct.c_fault &= ~BMS_FAULT_CURRENT;
         }
      }
   }

   /*------------------------------------------------------------------------*
    * Have all the faults been cleared?
    *------------------------------------------------------------------------*/
   if( gt_bmsObjct.c_fault == 0)
   {

      arb_ioctl( gt_bmsObjct.t_bmsHndl,
                 BMS_FAN_CNTRL,
                 BMS_DEVICE_OFF);

      arb_ioctl( gt_bmsObjct.t_hmi.t_lcdHndl,
                 LCD_BACKLIGHT_OFF,
                 0);

      /*---------------------------------------------------------------------*
       * Return to the previous state from which we came...
       *---------------------------------------------------------------------*/
      utl_stMnChangeState( gt_bmsObjct.t_stateMn,
                           (uint8_t)gt_bmsObjct.t_prevState,
                           0);
   }

}/*End usr_bmsFault*/

static void usr_bmsPowerDown( int32_t i_stateData,
                              uint8_t c_prevState)
{
   t_eepromError t_err;

   if( c_prevState != BMS_POWER_DOWN)
   {

//TODO shutoff charger here...

      arb_ioctl( gt_bmsObjct.t_bmsHndl,
                 BMS_FAN_CNTRL,
                 BMS_DEVICE_OFF);

      arb_ioctl( gt_bmsObjct.t_bmsHndl,
                 BMS_PACK_CNTRL,
                 BMS_DEVICE_OFF);

      arb_ioctl( gt_bmsObjct.t_signalHndl,
                 ALL_LEDS_OFF,
                 0);

      arb_ioctl( gt_bmsObjct.t_hmi.t_lcdHndl,
                 LCD_CLEAR,
                 0);

      arb_ioctl( gt_bmsObjct.t_hmi.t_lcdHndl,
                 LCD_CURSOR_HOME,
                 0);

      arb_ioctl( gt_bmsObjct.t_hmi.t_lcdHndl,
                 LCD_BACKLIGHT_OFF,
                 0);

      gt_bmsObjct.t_currState = BMS_POWER_DOWN;

      /*---------------------------------------------------------------------*
       * Keep track of where we came from...
       *---------------------------------------------------------------------*/
      gt_bmsObjct.t_prevState = (t_bmsState)c_prevState;

      /*---------------------------------------------------------------------*
       * Store the current settings into EEPROM.
       *---------------------------------------------------------------------*/
      t_err = utl_writeEeprom( gt_bmsObjct.t_eepromHndl,
                               (uint8_t *)&gt_bmsObjct.t_eeprom,
                               (uint16_t)sizeof( gt_bmsObjct.t_eeprom));

      if( t_err < 0)
      {
         arb_printf( PRINTF_DBG_HIGH,
                     "Failed to write EEPROM\n\r");
         exit(0);
      }

      /*---------------------------------------------------------------------*
       * Power off the motherboard...
       *---------------------------------------------------------------------*/
      arb_ioctl( gt_bmsObjct.t_bmsHndl,
                 BMS_PWR_CNTRL,
                 BMS_DEVICE_OFF);

      /*---------------------------------------------------------------------*
       * Wait for power down....
       *---------------------------------------------------------------------*/
      arb_sleep( ARB_TICKS_PER_SECOND*10);

   }

}/*End usr_bmsPowerDown*/

static void usr_bmsCheckForMessage( void)
{
   int8_t ac_message[BMS_MAILBOX_SIZE];
   int16_t s_numBytes;
   t_stmnError t_stErr;

   s_numBytes = arb_mailboxRead( gt_bmsObjct.t_bmsInMbxHndl,
                                 ac_message,
                                 (uint16_t)sizeof( ac_message));

   if( s_numBytes > 0)
   {
      switch( (t_bmsMsgId)ac_message[0])
      {
         case BMS_CMD_PWR_DOWN:

            /*---------------------------------------------------------------*
             * Transition to the power down state.
             *---------------------------------------------------------------*/
            t_stErr = utl_stMnChangeState( gt_bmsObjct.t_stateMn,
                                           (uint8_t)BMS_POWER_DOWN,
                                           0);
         break;

         case BMS_CMD_SET_LIMITS:
         {
            volatile t_bmsSetLimitsMsg *pt_bmsLimits =
            (t_bmsSetLimitsMsg *)&ac_message[0];

            if( usr_setBmsLimits( (t_bmsLimits *)&pt_bmsLimits->t_limits) ==
                false)
            {

            }
            else
            {

            }
         }
         break;

         case BMS_CMD_PACK_CNTRL:
         {
            volatile t_bmsPacCntrlMsg *pt_pacCntrl =
            (t_bmsPacCntrlMsg *)&ac_message[0];

            /*---------------------------------------------------------------*
             * Turns on/off the battery discharge FET which controls the output
             * of the battery pack.
             *---------------------------------------------------------------*/
            if( pt_pacCntrl->b_enabled == true)
            {
               arb_ioctl( gt_bmsObjct.t_bmsHndl,
                          BMS_PACK_CNTRL,
                          BMS_DEVICE_ON);
            }/*End if( pt_pacCntrl->b_enabled == true)*/
            else
            {
               arb_ioctl( gt_bmsObjct.t_bmsHndl,
                          BMS_PACK_CNTRL,
                          BMS_DEVICE_OFF);
            }
         }
         break;

         case BMS_CMD_FAULT_ACK:

            if( gt_bmsObjct.c_fault & BMS_FAULT_CURRENT)
            {
               if( fabs(gt_bmsObjct.t_bMeas.f_current) < gt_bmsObjct.t_eeprom.t_limits.
                  f_highCurrThr)
               {
                  gt_bmsObjct.c_fault &= ~BMS_FAULT_CURRENT;
               }
            }

            if( gt_bmsObjct.c_fault & BMS_FAULT_CHARGE)
               gt_bmsObjct.c_fault &= ~BMS_FAULT_CHARGE;

         break;

         case BMS_CMD_SET_BATT_SERIAL_NUM:

         break;

         case BMS_CMD_SET_BOARD_SERIAL_NUM:

         break;

         case BMS_CMD_GET_STATE:
            //Return the current state of the BMS...BMS_CMD_CURR_STATE
         break;

         case BMS_CMD_SET_CMB_CNT:
         {
            volatile t_bmsSetCmbCntMsg *pt_cmbCountMsg =
            (t_bmsSetCmbCntMsg *)&ac_message[0];

            if( (pt_cmbCountMsg->f_percFull <= BMS_FULL_SOC_PERC) &&
                (pt_cmbCountMsg->f_percFull >= BMS_EMPTY_SOC_PERC))
            {
               gt_bmsObjct.t_eeprom.l_battLevelCoulombs = (int64_t)(gt_bmsObjct.
               t_eeprom.t_limits.f_battAmpHours*3600.0f*
               pt_cmbCountMsg->f_percFull*32767.0f);

               /*------------------------------------------------------------*
                * Store the current settings into EEPROM.
                *------------------------------------------------------------*/
               utl_writeEeprom( gt_bmsObjct.t_eepromHndl,
                                (uint8_t *)&gt_bmsObjct.t_eeprom,
                                (uint16_t)sizeof( gt_bmsObjct.t_eeprom));

            }
         }
         break;

         case BMS_CMD_FAN_CNTRL:
         {
            volatile t_bmsFanCntrlMsg *pt_fanCntrl =
            (t_bmsFanCntrlMsg *)&ac_message[0];

            /*---------------------------------------------------------------*
             * Turns on/off the fan.
             *---------------------------------------------------------------*/
            if( pt_fanCntrl->b_enabled == true)
            {
               arb_ioctl( gt_bmsObjct.t_bmsHndl,
                          BMS_FAN_CNTRL,
                          BMS_DEVICE_ON);
            }/*End if( pt_fanCntrl->b_enabled == true)*/
            else
            {
               arb_ioctl( gt_bmsObjct.t_bmsHndl,
                          BMS_FAN_CNTRL,
                          BMS_DEVICE_OFF);
            }
         }
         break;

         default:
            /*---------------------------------------------------------------*
             * Unknown command.
             *---------------------------------------------------------------*/
         break;

      }/*End switch( (t_bmsMsgId)ac_message[0])*/

   }/*End if( s_numBytes > 0)*/

}/*End usr_bmsCheckForMessage*/

static void usr_bms( t_parameters t_param,
                     t_arguments  t_args)
{
   t_stmnError t_stErr;

   while( RUN_FOREVER)
   {

      /*---------------------------------------------------------------------*
       * Global data is being accessed, apply lock...
       *---------------------------------------------------------------------*/
      arb_wait( gt_bmsObjct.t_mutex, 0);

      /*---------------------------------------------------------------------*
       * Has the back light, menu, or power down button been pressed?
       *---------------------------------------------------------------------*/
      usr_checkForButtonPress();

      /*---------------------------------------------------------------------*
       * Check for a new message being sent to the BMS.
       *---------------------------------------------------------------------*/
      usr_bmsCheckForMessage();

      /*---------------------------------------------------------------------*
       * Collect new, voltage, current, and temperature measurements as well
       * as update the BMS time step and Coulomb count.
       *---------------------------------------------------------------------*/
      usr_bmsCollectMeas();

      /*---------------------------------------------------------------------*
       * Check for any issues with the system.
       *---------------------------------------------------------------------*/
      usr_bmsFaultMonitor();

      /*---------------------------------------------------------------------*
       * Transition to the next state...
       *---------------------------------------------------------------------*/
      t_stErr = utl_stMnEngine( gt_bmsObjct.t_stateMn);

      /*---------------------------------------------------------------------*
       * Clear the back light and menu button indicators...
       *---------------------------------------------------------------------*/
      usr_clearButtonPress();

      /*---------------------------------------------------------------------*
       * unlock...
       *---------------------------------------------------------------------*/
      arb_signal( gt_bmsObjct.t_mutex);

      /*---------------------------------------------------------------------*
       * Allow other threads to run...
       *---------------------------------------------------------------------*/
      arb_sleep( BMS_SLEEP_TICKS);

   }/*End while( RUN_FOREVER)*/

}/*End usr_bms*/

static void usr_bmsStateMachineInit( void)
{
   uint8_t ac_transMap[BMS_NUM_STATES][BMS_NUM_STATES];

   /*------------------------------------------------------------------------*
    * Request access to a new state-machine object.
    *------------------------------------------------------------------------*/
   gt_bmsObjct.t_stateMn = utl_requestStMnObject( BMS_NUM_STATES,
                                                  BMS_BOOT);
   if( gt_bmsObjct.t_stateMn < 0)
      exit(0);

   /*------------------------------------------------------------------------*
    * Store the call-back function for each possible state transition.
    *------------------------------------------------------------------------*/
   if( utl_stMnPopFunMap( gt_bmsObjct.t_stateMn,
                          &usr_bmsBoot,
                          (uint8_t)BMS_BOOT) < 0)
   {
      exit(0);
   }

   if( utl_stMnPopFunMap( gt_bmsObjct.t_stateMn,
                          &usr_bmsNominal,
                          (uint8_t)BMS_NOMINAL) < 0)
   {
      exit(0);
   }

   if( utl_stMnPopFunMap( gt_bmsObjct.t_stateMn,
                          &usr_bmsDeepDist,
                          (uint8_t)BMS_DEEP_DIST) < 0)
   {
      exit(0);
   }

   if( utl_stMnPopFunMap( gt_bmsObjct.t_stateMn,
                          &usr_bmsPowerDown,
                          (uint8_t)BMS_POWER_DOWN) < 0)
   {
      exit(0);
   }

   if( utl_stMnPopFunMap( gt_bmsObjct.t_stateMn,
                          &usr_bmsFault,
                          (uint8_t)BMS_FAULT) < 0)
   {
      exit(0);
   }

   if( utl_stMnPopFunMap( gt_bmsObjct.t_stateMn,
                          &usr_bmsChargeDeep,
                          (uint8_t)BMS_CHARGE_DEEP) < 0)
   {
      exit(0);
   }

   if( utl_stMnPopFunMap( gt_bmsObjct.t_stateMn,
                          &usr_bmsChargeNom,
                          (uint8_t)BMS_CHARGE_NOM) < 0)
   {
      exit(0);
   }

   memset( (void *)ac_transMap, 0, sizeof( ac_transMap));

   /*------------------------------------------------------------------------*
    * Define all possible state transitions...
    *------------------------------------------------------------------------*/
   ac_transMap[BMS_BOOT][BMS_BOOT]      = 1;
   ac_transMap[BMS_BOOT][BMS_NOMINAL]   = 1;
   ac_transMap[BMS_BOOT][BMS_DEEP_DIST] = 1;
   ac_transMap[BMS_BOOT][BMS_FAULT]     = 1;

   ac_transMap[BMS_NOMINAL][BMS_NOMINAL]    = 1;
   ac_transMap[BMS_NOMINAL][BMS_DEEP_DIST]  = 1;
   ac_transMap[BMS_NOMINAL][BMS_POWER_DOWN] = 1;
   ac_transMap[BMS_NOMINAL][BMS_FAULT]      = 1;
   ac_transMap[BMS_NOMINAL][BMS_CHARGE_NOM] = 1;

   ac_transMap[BMS_DEEP_DIST][BMS_DEEP_DIST]   = 1;
   ac_transMap[BMS_DEEP_DIST][BMS_POWER_DOWN]  = 1;
   ac_transMap[BMS_DEEP_DIST][BMS_CHARGE_DEEP] = 1;

   ac_transMap[BMS_FAULT][BMS_FAULT]       = 1;
   ac_transMap[BMS_FAULT][BMS_BOOT]        = 1;
   ac_transMap[BMS_FAULT][BMS_NOMINAL]     = 1;
   ac_transMap[BMS_FAULT][BMS_POWER_DOWN]  = 1;
   ac_transMap[BMS_FAULT][BMS_CHARGE_DEEP] = 1;
   ac_transMap[BMS_FAULT][BMS_CHARGE_NOM]  = 1;

   ac_transMap[BMS_CHARGE_DEEP][BMS_CHARGE_DEEP] = 1;
   ac_transMap[BMS_CHARGE_DEEP][BMS_CHARGE_NOM]  = 1;
   ac_transMap[BMS_CHARGE_DEEP][BMS_FAULT]       = 1;
   ac_transMap[BMS_CHARGE_DEEP][BMS_DEEP_DIST]   = 1;
   ac_transMap[BMS_CHARGE_DEEP][BMS_POWER_DOWN]  = 1;

   ac_transMap[BMS_CHARGE_NOM][BMS_CHARGE_NOM] = 1;
   ac_transMap[BMS_CHARGE_NOM][BMS_NOMINAL]    = 1;
   ac_transMap[BMS_CHARGE_NOM][BMS_FAULT]      = 1;
   ac_transMap[BMS_CHARGE_NOM][BMS_POWER_DOWN] = 1;

   /*------------------------------------------------------------------------*
    * Store the state transition map.
    *------------------------------------------------------------------------*/
   utl_stMnPopTransMap( gt_bmsObjct.t_stateMn,
                        &ac_transMap[0][0]);

}/*End usr_bmsStateMachineInit*/

/*---------------------------------------------------------------------------*
 * Public Function Definitions
 *---------------------------------------------------------------------------*/

/*---------------------------------------------------------------------------*
 * Returns the last known battery voltage, current, temperature, fuel gage
 * and charging status.
 *---------------------------------------------------------------------------*/
t_bmsMeasStatus usr_getBmsMeas( void)
{
   t_bmsMeasStatus t_status;

   t_status.t_meas     = gt_bmsObjct.t_bMeas;
   t_status.t_state    = gt_bmsObjct.t_currState;
   t_status.f_gasGuage = gt_bmsObjct.f_gasGuage;
   t_status.c_fault    = gt_bmsObjct.c_fault;

   return t_status;

}/*End usr_getBmsMeas*/

/*---------------------------------------------------------------------------*
 * This function returns the mailbox handle for sending messages to the BMS.
 *---------------------------------------------------------------------------*/
t_MAILBOXHNDL usr_getBmsInMlbxHndle( void)
{
   return gt_bmsObjct.t_bmsInMbxHndl;
}/*End usr_getBmsInMlbxHndle*/

/*---------------------------------------------------------------------------*
 * This function returns the mailbox handle for receiving messages from the
 * BMS.
 *---------------------------------------------------------------------------*/
t_MAILBOXHNDL usr_getBmsOutMlbxHndle( void)
{
   return gt_bmsObjct.t_bmsOutMbxHndl;
}/*End usr_getBmsOutMlbxHndle*/

t_error usr_bmsInit( void)
{
   t_mailboxConfig t_mConfig;
   t_gpioConf t_conf;

   /*------------------------------------------------------------------------*
    * Initialize the BMS object...
    *------------------------------------------------------------------------*/
   memset( (void *)&gt_bmsObjct, 0, sizeof( t_bmsObjct));

   /*------------------------------------------------------------------------*
    * Create a new thread.
    *------------------------------------------------------------------------*/
   gt_bmsObjct.t_bmsThread = arb_threadCreate( usr_bms,
                                               1,
                                               0,
                                               ARB_STACK_640B,
                                               0);

   if( gt_bmsObjct.t_bmsThread < 0)
   {
      return gt_bmsObjct.t_bmsThread;

   }/*End if( gt_bmsObjct.t_bmsThread < 0)*/

   /*------------------------------------------------------------------------*
    * Open a handle to the console driver.
    *------------------------------------------------------------------------*/
   gt_bmsObjct.t_signalHndl = arb_open( "signalDevice0",
                                        ARB_O_READ |
                                        ARB_O_WRITE);

   if( gt_bmsObjct.t_signalHndl < 0)
   {
      return gt_bmsObjct.t_signalHndl;
   }

   /*------------------------------------------------------------------------*
    * Request access to a semaphore which will be used for locking access
    * to the BMS object.
    *------------------------------------------------------------------------*/
   gt_bmsObjct.t_mutex = arb_semaphoreCreate( MUTEX);

   if( gt_bmsObjct.t_mutex < 0)
   {
      return gt_bmsObjct.t_mutex;
   }

   /*------------------------------------------------------------------------*
    * Request access to a semaphore which will be used for letting the BMS
    * know that the user has properly acknowledged an error condition.
    *------------------------------------------------------------------------*/
   gt_bmsObjct.t_bmsLimitAck = arb_semaphoreCreate( SIGNAL);

   if( gt_bmsObjct.t_bmsLimitAck < 0)
   {
      return gt_bmsObjct.t_bmsLimitAck;
   }

   /*------------------------------------------------------------------------*
    * Open a handle to the BMS driver.
    *------------------------------------------------------------------------*/
   gt_bmsObjct.t_bmsHndl = arb_open( "bmsDevice0",
                                     ARB_O_READ |
                                     ARB_O_WRITE);

   if( gt_bmsObjct.t_bmsHndl < 0)
   {
      return gt_bmsObjct.t_bmsHndl;
   }

   /*------------------------------------------------------------------------*
    * Create a mailbox for incoming messages to the BMS.
    *------------------------------------------------------------------------*/
   t_mConfig.s_queueSize  = BMS_MAILBOX_SIZE;
   t_mConfig.s_queueDepth = 1;
   t_mConfig.t_writeMode  = BLOCKING;    /*Threads writing to BMS block*/
   t_mConfig.t_readMode   = NONBLOCKING; /*BMS reading does not block*/
   t_mConfig.b_wrtFromInt = false;
   t_mConfig.b_multRdWr   = true;

   gt_bmsObjct.t_bmsInMbxHndl = arb_mailboxCreate( t_mConfig);

   if( gt_bmsObjct.t_bmsInMbxHndl < 0)
   {
      return gt_bmsObjct.t_bmsInMbxHndl;
   }

   /*------------------------------------------------------------------------*
    * Create a mailbox for outgoing messages from the BMS.
    *------------------------------------------------------------------------*/
   t_mConfig.s_queueSize  = BMS_MAILBOX_SIZE;
   t_mConfig.s_queueDepth = 1;
   t_mConfig.t_writeMode  = NONBLOCKING; /*BMS writing does not block*/
   t_mConfig.t_readMode   = BLOCKING;    /*Threads reading from BMS block*/
   t_mConfig.b_wrtFromInt = false;
   t_mConfig.b_multRdWr   = true;

   gt_bmsObjct.t_bmsOutMbxHndl = arb_mailboxCreate( t_mConfig);

   if( gt_bmsObjct.t_bmsOutMbxHndl < 0)
   {
      return gt_bmsObjct.t_bmsOutMbxHndl;
   }

   /*------------------------------------------------------------------------*
    * Request access to a block of EEPROM...
    *------------------------------------------------------------------------*/
   gt_bmsObjct.t_eepromHndl =
   utl_requestEepromBlock( BMS_EEPROM_ID,
                           (int16_t)sizeof( gt_bmsObjct.t_eeprom));

   /*------------------------------------------------------------------------*
    * Has the block already been allocated?
    *------------------------------------------------------------------------*/
   if( gt_bmsObjct.t_eepromHndl == EEPROM_ALREADY_ACTIVE_USERID)
   {
      gt_bmsObjct.t_eepromHndl = utl_getEepromHandle( BMS_EEPROM_ID);
      if( gt_bmsObjct.t_eepromHndl < 0)
      {
         return gt_bmsObjct.t_eepromHndl;
      }

   }/*End if( gt_bmsObjct.t_eepromHndl == EEPROM_ALREADY_ACTIVE_USERID)*/
   else
   {
      usr_storeEepromFactDefaults();
   }

   usr_bmsStateMachineInit();

   /*------------------------------------------------------------------------*
    * See if there are volt, current, and temp meter calibration parameters,
    * as well as battery limits stored in EEPROM. If not, load the factory
    * default parameters.
    *------------------------------------------------------------------------*/
   usr_getValuesFromEeprom();

   /*------------------------------------------------------------------------*
    * Open a handle to the BMS driver.
    *------------------------------------------------------------------------*/
   gt_bmsObjct.t_hmi.t_lcdHndl = arb_open( "lcdDevice0",
                                            ARB_O_READ |
                                            ARB_O_WRITE);

   if( gt_bmsObjct.t_hmi.t_lcdHndl < 0)
   {
      return gt_bmsObjct.t_hmi.t_lcdHndl;
   }

   /*------------------------------------------------------------------------*
    * Tell the driver the current calibration settings.
    *------------------------------------------------------------------------*/
   arb_ioctl( gt_bmsObjct.t_bmsHndl,
              BMS_STORE_CAL,
              (uint32_t)((uint16_t)&gt_bmsObjct.t_eeprom.t_cal));

   t_conf.c_inputMask  = HMI_BACKLIGHT_PIN | HMI_MENU_PIN | HMI_PWR_PIN;
   t_conf.c_outputMask = 0;
   t_conf.t_inConf     = PULLDOWN;
   t_conf.t_outConf    = TOTEM;

   if( utl_configureGpioPort( HMI_BUTTON_PORT, t_conf) < 0)
   {
      return ARB_UTL_ERROR;
   }

   /*------------------------------------------------------------------------*
    * Configure the high and low Coulomb count limits...
    *------------------------------------------------------------------------*/
   gt_bmsObjct.l_batteryEmptyCoulombLevel = (int64_t)(gt_bmsObjct.t_eeprom.
   t_limits.f_battAmpHours*3600.0f*BMS_EMPTY_SOC_PERC*32767.0f);

   gt_bmsObjct.l_batteryFullCoulombLevel = (int64_t)(gt_bmsObjct.t_eeprom.
   t_limits.f_battAmpHours*3600.0f*BMS_FULL_SOC_PERC*32767.0f);

   return ARB_PASSED;

}/*End usr_bmsInit*/
